mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-12-07 14:09:47 +00:00
List of currently supported filters: - `is:open` (or `-is:closed`) - `is:closed` (or `-is:open`) - `is:all` - `author:<username>` - `assignee:<username>` - `review:<username>` - `mentions:<username>` - `modified:[>|<]<date>`, where `<date>` is the last update date. - `sort:<by>:[asc|desc]`, where `<by>` is among - created - comments - updated - deadline Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9109 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Ellen Εμιλία Άννα Zscheile <fogti@noreply.codeberg.org> Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: Shiny Nematoda <snematoda.751k2@aleeas.com> Co-committed-by: Shiny Nematoda <snematoda.751k2@aleeas.com>
465 lines
7.6 KiB
Go
465 lines
7.6 KiB
Go
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package internal
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"forgejo.org/models/unittest"
|
|
"forgejo.org/models/user"
|
|
"forgejo.org/modules/optional"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type testIssueQueryStringOpt struct {
|
|
Keyword string
|
|
Results []Token
|
|
}
|
|
|
|
var testOpts = []testIssueQueryStringOpt{
|
|
{
|
|
Keyword: "Hello",
|
|
Results: []Token{
|
|
{
|
|
Term: "Hello",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "Hello World",
|
|
Results: []Token{
|
|
{
|
|
Term: "Hello",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
{
|
|
Term: "World",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "Hello World",
|
|
Results: []Token{
|
|
{
|
|
Term: "Hello",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
{
|
|
Term: "World",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: " Hello World ",
|
|
Results: []Token{
|
|
{
|
|
Term: "Hello",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
{
|
|
Term: "World",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "+Hello +World",
|
|
Results: []Token{
|
|
{
|
|
Term: "Hello",
|
|
Fuzzy: true,
|
|
Kind: BoolOptMust,
|
|
},
|
|
{
|
|
Term: "World",
|
|
Fuzzy: true,
|
|
Kind: BoolOptMust,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "+Hello World",
|
|
Results: []Token{
|
|
{
|
|
Term: "Hello",
|
|
Fuzzy: true,
|
|
Kind: BoolOptMust,
|
|
},
|
|
{
|
|
Term: "World",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "+Hello -World",
|
|
Results: []Token{
|
|
{
|
|
Term: "Hello",
|
|
Fuzzy: true,
|
|
Kind: BoolOptMust,
|
|
},
|
|
{
|
|
Term: "World",
|
|
Fuzzy: true,
|
|
Kind: BoolOptNot,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "\"Hello World\"",
|
|
Results: []Token{
|
|
{
|
|
Term: "Hello World",
|
|
Fuzzy: false,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "+\"Hello World\"",
|
|
Results: []Token{
|
|
{
|
|
Term: "Hello World",
|
|
Fuzzy: false,
|
|
Kind: BoolOptMust,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "-\"Hello World\"",
|
|
Results: []Token{
|
|
{
|
|
Term: "Hello World",
|
|
Fuzzy: false,
|
|
Kind: BoolOptNot,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "\"+Hello -World\"",
|
|
Results: []Token{
|
|
{
|
|
Term: "+Hello -World",
|
|
Fuzzy: false,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "\\+Hello", // \+Hello => +Hello
|
|
Results: []Token{
|
|
{
|
|
Term: "+Hello",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "\\\\Hello", // \\Hello => \Hello
|
|
Results: []Token{
|
|
{
|
|
Term: "\\Hello",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "\\\"Hello", // \"Hello => "Hello
|
|
Results: []Token{
|
|
{
|
|
Term: "\"Hello",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "\\",
|
|
Results: nil,
|
|
},
|
|
{
|
|
Keyword: "\"",
|
|
Results: nil,
|
|
},
|
|
{
|
|
Keyword: "Hello \\",
|
|
Results: []Token{
|
|
{
|
|
Term: "Hello",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "\"\"",
|
|
Results: nil,
|
|
},
|
|
{
|
|
Keyword: "\" World \"",
|
|
Results: []Token{
|
|
{
|
|
Term: " World ",
|
|
Fuzzy: false,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "\"\" World \"\"",
|
|
Results: []Token{
|
|
{
|
|
Term: "World",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "Best \"Hello World\" Ever",
|
|
Results: []Token{
|
|
{
|
|
Term: "Best",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
{
|
|
Term: "Hello World",
|
|
Fuzzy: false,
|
|
Kind: BoolOptShould,
|
|
},
|
|
{
|
|
Term: "Ever",
|
|
Fuzzy: true,
|
|
Kind: BoolOptShould,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestIssueQueryString(t *testing.T) {
|
|
var opt SearchOptions
|
|
ctx := t.Context()
|
|
for _, res := range testOpts {
|
|
t.Run(res.Keyword, func(t *testing.T) {
|
|
require.NoError(t, opt.WithKeyword(ctx, res.Keyword))
|
|
assert.Equal(t, res.Results, opt.Tokens)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMain(m *testing.M) {
|
|
unittest.MainTest(m)
|
|
}
|
|
|
|
func TestIssueQueryStringWithFilters(t *testing.T) {
|
|
// we don't need all the fixures
|
|
// insert only one single test user
|
|
require.NoError(t, user.CreateUser(t.Context(), &user.User{
|
|
ID: 2,
|
|
Name: "test",
|
|
LowerName: "test",
|
|
Email: "test@localhost",
|
|
}))
|
|
|
|
for _, c := range []struct {
|
|
Keyword string
|
|
Opts *SearchOptions
|
|
}{
|
|
// Generic Cases
|
|
{
|
|
Keyword: "modified:>2025-08-28",
|
|
Opts: &SearchOptions{
|
|
UpdatedAfterUnix: optional.Some(int64(1756339200)),
|
|
},
|
|
},
|
|
{
|
|
Keyword: "modified:<2025-08-28",
|
|
Opts: &SearchOptions{
|
|
UpdatedBeforeUnix: optional.Some(int64(1756339200)),
|
|
},
|
|
},
|
|
{
|
|
Keyword: "modified:>2025-08-28 modified:<2025-08-28",
|
|
Opts: &SearchOptions{
|
|
UpdatedAfterUnix: optional.Some(int64(1756339200)),
|
|
UpdatedBeforeUnix: optional.Some(int64(1756339200)),
|
|
},
|
|
},
|
|
{
|
|
Keyword: "modified:2025-08-28",
|
|
Opts: &SearchOptions{
|
|
UpdatedAfterUnix: optional.Some(int64(1756339200)),
|
|
UpdatedBeforeUnix: optional.Some(int64(1756339200)),
|
|
},
|
|
},
|
|
{
|
|
Keyword: "assignee:test",
|
|
Opts: &SearchOptions{
|
|
AssigneeID: optional.Some(int64(2)),
|
|
},
|
|
},
|
|
{
|
|
Keyword: "assignee:test hi",
|
|
Opts: &SearchOptions{
|
|
AssigneeID: optional.Some(int64(2)),
|
|
Tokens: []Token{
|
|
{
|
|
Term: "hi",
|
|
Kind: BoolOptShould,
|
|
Fuzzy: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "mentions:test",
|
|
Opts: &SearchOptions{
|
|
MentionID: optional.Some(int64(2)),
|
|
},
|
|
},
|
|
{
|
|
Keyword: "review:test",
|
|
Opts: &SearchOptions{
|
|
ReviewedID: optional.Some(int64(2)),
|
|
},
|
|
},
|
|
{
|
|
Keyword: "author:test",
|
|
Opts: &SearchOptions{
|
|
PosterID: optional.Some(int64(2)),
|
|
},
|
|
},
|
|
{
|
|
Keyword: "sort:updated:asc",
|
|
Opts: &SearchOptions{
|
|
SortBy: SortByUpdatedAsc,
|
|
},
|
|
},
|
|
{
|
|
Keyword: "sort:test",
|
|
Opts: &SearchOptions{
|
|
SortBy: SortByScore,
|
|
},
|
|
},
|
|
{
|
|
Keyword: "test author:test mentions:test modified:<2025-08-28 sort:comments:desc",
|
|
Opts: &SearchOptions{
|
|
Tokens: []Token{
|
|
{
|
|
Term: "test",
|
|
Kind: BoolOptShould,
|
|
Fuzzy: true,
|
|
},
|
|
},
|
|
MentionID: optional.Some(int64(2)),
|
|
PosterID: optional.Some(int64(2)),
|
|
UpdatedBeforeUnix: optional.Some(int64(1756339200)),
|
|
SortBy: SortByCommentsDesc,
|
|
},
|
|
},
|
|
|
|
// Edge Cases
|
|
{
|
|
Keyword: "author:",
|
|
Opts: &SearchOptions{
|
|
Tokens: []Token{
|
|
{
|
|
Term: "author:",
|
|
Kind: BoolOptShould,
|
|
Fuzzy: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "author:testt",
|
|
Opts: &SearchOptions{},
|
|
},
|
|
{
|
|
Keyword: "author: test",
|
|
Opts: &SearchOptions{
|
|
Tokens: []Token{
|
|
{
|
|
Term: "author:",
|
|
Kind: BoolOptShould,
|
|
Fuzzy: true,
|
|
},
|
|
{
|
|
Term: "test",
|
|
Kind: BoolOptShould,
|
|
Fuzzy: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Keyword: "modified:",
|
|
Opts: &SearchOptions{
|
|
Tokens: []Token{
|
|
{
|
|
Term: "modified:",
|
|
Kind: BoolOptShould,
|
|
Fuzzy: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
} {
|
|
t.Run(c.Keyword, func(t *testing.T) {
|
|
opts := &SearchOptions{}
|
|
require.NoError(t, opts.WithKeyword(context.Background(), c.Keyword))
|
|
assert.Equal(t, c.Opts, opts)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestToken_ParseIssueReference(t *testing.T) {
|
|
var tk Token
|
|
{
|
|
tk.Term = "123"
|
|
id, err := tk.ParseIssueReference()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(123), id)
|
|
}
|
|
{
|
|
tk.Term = "#123"
|
|
id, err := tk.ParseIssueReference()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(123), id)
|
|
}
|
|
{
|
|
tk.Term = "!123"
|
|
id, err := tk.ParseIssueReference()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(123), id)
|
|
}
|
|
{
|
|
tk.Term = "text"
|
|
_, err := tk.ParseIssueReference()
|
|
require.Error(t, err)
|
|
}
|
|
{
|
|
tk.Term = ""
|
|
_, err := tk.ParseIssueReference()
|
|
require.Error(t, err)
|
|
}
|
|
}
|