blob: 378a7d8ed4f66221ac7ca7e20a77c73a4cb1d5c0 [file] [log] [blame]
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"context"
"fmt"
"net/http"
"strconv"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/safehtml"
"golang.org/x/oscar/internal/docs"
"golang.org/x/oscar/internal/embeddocs"
"golang.org/x/oscar/internal/github"
"golang.org/x/oscar/internal/llmapp"
)
func TestPopulateOverviewPage(t *testing.T) {
g := newTestGaby(t)
// Add test data relevant to this test.
project := "hello/world"
g.githubProjects = []string{project}
g.github.Add(project)
iss1 := &github.Issue{
Number: 1,
Title: "hello",
Body: "hello world",
}
iss2 := &github.Issue{
Number: 2,
Title: "hello 2",
Body: "hello world 2",
}
comment := &github.IssueComment{
Body: "a comment",
}
comment2 := &github.IssueComment{
Body: "another comment",
}
// Note: these calls populate the ID, HTMLURL and URL fields
// of the issues and comments.
g.github.Testing().AddIssue(project, iss1)
g.github.Testing().AddIssueComment(project, 1, comment)
g.github.Testing().AddIssueComment(project, 1, comment2)
g.github.Testing().AddIssue(project, iss2)
commentID := strconv.Itoa(int(comment.CommentID()))
ctx := context.Background()
docs.Sync(g.docs, g.github)
embeddocs.Sync(ctx, g.slog, g.vector, g.embed, g.docs)
// Generate expected overviews.
wantIssueOverview, err := g.llm.PostOverview(ctx, &llmapp.Doc{
Type: "issue",
URL: iss1.HTMLURL,
Title: iss1.Title,
Text: iss1.Body,
}, []*llmapp.Doc{
{
Type: "issue comment",
URL: comment.HTMLURL,
Text: comment.Body,
},
{
Type: "issue comment",
URL: comment2.HTMLURL,
Text: comment2.Body,
},
})
if err != nil {
t.Fatal(err)
}
wantUpdateOverview, err := g.llm.UpdatedPostOverview(ctx, &llmapp.Doc{
Type: "issue",
URL: iss1.HTMLURL,
Title: iss1.Title,
Text: iss1.Body,
}, []*llmapp.Doc{
{
Type: "issue comment",
URL: comment.HTMLURL,
Text: comment.Body,
},
}, []*llmapp.Doc{
{
Type: "issue comment",
URL: comment2.HTMLURL,
Text: comment2.Body,
},
})
if err != nil {
t.Fatal(err)
}
wantRelatedOverview, err := g.llm.RelatedOverview(ctx, &llmapp.Doc{
Type: "main",
URL: iss1.HTMLURL,
Title: iss1.Title,
Text: iss1.Body,
}, []*llmapp.Doc{
{
Type: "related",
URL: iss2.HTMLURL,
Title: iss2.Title,
Text: iss2.Body,
},
})
if err != nil {
t.Fatal(err)
}
for _, tc := range []struct {
name string
r *http.Request
want *overviewPage
}{
{
name: "empty",
r: &http.Request{},
want: &overviewPage{},
},
{
name: "issue overview (default)",
r: &http.Request{
Form: map[string][]string{
"q": {"1"},
},
},
want: &overviewPage{
Params: overviewParams{
Query: "1",
OverviewType: "",
},
Result: &overviewResult{
IssueOverviewResult: github.IssueOverviewResult{
Issue: iss1,
TotalComments: 2,
Overview: wantIssueOverview,
},
Type: issueOverviewType,
Desc: "issue 1 and all 2 comments",
},
},
},
{
name: "issue overview (explicit)",
r: &http.Request{
Form: map[string][]string{
"q": {"1"},
"t": {issueOverviewType},
},
},
want: &overviewPage{
Params: overviewParams{
Query: "1",
OverviewType: issueOverviewType,
},
Result: &overviewResult{
IssueOverviewResult: github.IssueOverviewResult{
Issue: iss1,
TotalComments: 2,
Overview: wantIssueOverview,
},
Type: issueOverviewType,
Desc: "issue 1 and all 2 comments",
},
},
},
{
name: "related overview",
r: &http.Request{
Form: map[string][]string{
"q": {"1"},
"t": {relatedOverviewType},
},
},
want: &overviewPage{
Params: overviewParams{
Query: "1",
OverviewType: relatedOverviewType,
},
Result: &overviewResult{
IssueOverviewResult: github.IssueOverviewResult{
Issue: iss1,
Overview: wantRelatedOverview,
},
Type: relatedOverviewType,
Desc: "issue 1 and related docs",
},
},
},
{
name: "update overview",
r: &http.Request{
Form: map[string][]string{
paramQuery: {"1"},
paramOverviewType: {updateOverviewType},
paramLastRead: {commentID},
},
},
want: &overviewPage{
Params: overviewParams{
Query: "1",
OverviewType: updateOverviewType,
LastReadComment: commentID,
},
Result: &overviewResult{
IssueOverviewResult: github.IssueOverviewResult{
Issue: iss1,
NewComments: 1,
TotalComments: 2,
Overview: wantUpdateOverview,
},
Type: updateOverviewType,
Desc: fmt.Sprintf("issue 1 and its 1 new comments after %d", comment.CommentID()),
},
},
},
{
name: "error/unknownIssue",
r: &http.Request{
Form: map[string][]string{
"q": {"3"}, // not in DB
"t": {relatedOverviewType},
},
},
want: &overviewPage{
Params: overviewParams{
Query: "3",
OverviewType: relatedOverviewType,
},
Error: cmpopts.AnyError,
},
},
{
name: "error/unknownProject",
r: &http.Request{
Form: map[string][]string{
"q": {"unknown/project#3"}, // not in DB
"t": {relatedOverviewType},
},
},
want: &overviewPage{
Params: overviewParams{
Query: "unknown/project#3",
OverviewType: relatedOverviewType,
},
Error: cmpopts.AnyError,
},
},
} {
t.Run(tc.name, func(t *testing.T) {
got := g.populateOverviewPage(tc.r)
tc.want.setCommonPage()
if diff := cmp.Diff(got, tc.want,
cmpopts.IgnoreFields(llmapp.OverviewResult{}, "Cached"),
cmpopts.EquateErrors(),
safeHTMLcmpopt); diff != "" {
t.Errorf("Gaby.populateOverviewPage() mismatch (-got +want):\n%s", diff)
}
})
}
}
var safeHTMLcmpopt = cmpopts.EquateComparable(safehtml.TrustedResourceURL{}, safehtml.Identifier{})
func TestParseOverviewPageQuery(t *testing.T) {
tests := []struct {
in string
wantProject string
wantIssue int64
wantErr bool
}{
{
in: "",
},
{
in: "12345",
wantIssue: 12345,
},
{
in: "golang/go#12345",
wantProject: "golang/go",
wantIssue: 12345,
},
{
in: " 123",
wantIssue: 123,
},
{
in: "x012x",
wantErr: true,
},
{
in: "golang/go",
wantErr: true,
},
{
in: "https://github.com/foo/bar/issues/12345",
wantProject: "foo/bar",
wantIssue: 12345,
},
{
in: "https://go.dev/issues/234",
wantProject: "golang/go",
wantIssue: 234,
},
{
in: "github.com/foo/bar/issues/12345",
wantProject: "foo/bar",
wantIssue: 12345,
},
{
in: "go.dev/issues/234",
wantProject: "golang/go",
wantIssue: 234,
},
{
in: "https://example.com/foo/bar/issues/12345",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.in, func(t *testing.T) {
proj, issue, err := parseIssueNumber(tt.in)
if (err != nil) != tt.wantErr {
t.Fatalf("parseOverviewPageQuery(%q) error = %v, wantErr %v", tt.in, err, tt.wantErr)
}
if proj != tt.wantProject || issue != tt.wantIssue {
t.Errorf("parseOverviewPageQuery(%q) = (%q, %q, %v), want (%q, %q, _)", tt.in, proj, issue, err, tt.wantProject, tt.wantIssue)
}
})
}
}