569340cf5fa42e3986981d057b955fb893418910 / internal/db/issues_test.go · 7535 bytes · raw
package db
import (
"errors"
"testing"
)
func TestCreateIssueNumbersPerRepo(t *testing.T) {
database := openTestDB(t)
ownerID, _ := CreateUser(database, "josie", "hash")
repoA, err := CreateRepo(database, ownerID, "alpha", "", "public")
if err != nil {
t.Fatalf("CreateRepo A: %v", err)
}
repoB, err := CreateRepo(database, ownerID, "beta", "", "public")
if err != nil {
t.Fatalf("CreateRepo B: %v", err)
}
first, err := CreateIssue(database, repoA.ID, "one", "body", ownerID, "josie", "", false)
if err != nil {
t.Fatalf("CreateIssue A1: %v", err)
}
second, err := CreateIssue(database, repoA.ID, "two", "body", ownerID, "josie", "", false)
if err != nil {
t.Fatalf("CreateIssue A2: %v", err)
}
other, err := CreateIssue(database, repoB.ID, "other", "body", 0, "guest", "g@example.com", true)
if err != nil {
t.Fatalf("CreateIssue B1: %v", err)
}
if first.Number != 1 || second.Number != 2 {
t.Errorf("repo A numbers = %d, %d, want 1, 2", first.Number, second.Number)
}
if other.Number != 1 {
t.Errorf("repo B number = %d, want 1 (independent)", other.Number)
}
if other.Pending != true {
t.Error("guest issue Pending = false, want true")
}
if other.AuthorID.Valid {
t.Error("guest issue AuthorID is set, want NULL")
}
if other.AuthorName != "guest" || other.AuthorEmail != "g@example.com" {
t.Errorf("guest author = %q/%q, want guest/g@example.com", other.AuthorName, other.AuthorEmail)
}
if !first.AuthorID.Valid || first.AuthorID.Int64 != ownerID {
t.Errorf("owner issue AuthorID = %+v, want %d", first.AuthorID, ownerID)
}
}
func TestGetIssueByNumberIsRepoScoped(t *testing.T) {
database := openTestDB(t)
ownerID, _ := CreateUser(database, "josie", "hash")
repoA, _ := CreateRepo(database, ownerID, "alpha", "", "public")
repoB, _ := CreateRepo(database, ownerID, "beta", "", "public")
if _, err := CreateIssue(database, repoA.ID, "a1", "", ownerID, "josie", "", false); err != nil {
t.Fatalf("CreateIssue: %v", err)
}
if _, err := GetIssueByNumber(database, repoB.ID, 1); !errors.Is(err, ErrNotFound) {
t.Errorf("GetIssueByNumber(other repo, 1) err = %v, want ErrNotFound", err)
}
}
func TestListIssuesPendingAndState(t *testing.T) {
database := openTestDB(t)
ownerID, _ := CreateUser(database, "josie", "hash")
repo, _ := CreateRepo(database, ownerID, "alpha", "", "public")
published, _ := CreateIssue(database, repo.ID, "published", "", ownerID, "josie", "", false)
pending, _ := CreateIssue(database, repo.ID, "pending", "", 0, "guest", "", true)
public, err := ListIssues(database, repo.ID, false, "")
if err != nil {
t.Fatalf("ListIssues public: %v", err)
}
if len(public) != 1 || public[0].Title != "published" {
t.Errorf("public list = %+v, want only the published issue", public)
}
owner, err := ListIssues(database, repo.ID, true, "")
if err != nil {
t.Fatalf("ListIssues owner: %v", err)
}
if len(owner) != 2 || !owner[0].Pending {
t.Errorf("owner list = %+v, want 2 with pending first", owner)
}
if err := SetIssueState(database, repo.ID, published.Number, "closed"); err != nil {
t.Fatalf("SetIssueState closed: %v", err)
}
closed, err := ListIssues(database, repo.ID, true, "closed")
if err != nil {
t.Fatalf("ListIssues closed: %v", err)
}
if len(closed) != 1 || closed[0].Number != published.Number {
t.Errorf("closed list = %+v, want the closed issue", closed)
}
open, err := ListIssues(database, repo.ID, true, "open")
if err != nil {
t.Fatalf("ListIssues open: %v", err)
}
if len(open) != 1 || open[0].Number != pending.Number {
t.Errorf("open list = %+v, want the pending issue", open)
}
reloaded, err := GetIssueByNumber(database, repo.ID, published.Number)
if err != nil {
t.Fatalf("GetIssueByNumber: %v", err)
}
if !reloaded.ClosedAt.Valid {
t.Error("ClosedAt is NULL after close")
}
if err := SetIssueState(database, repo.ID, published.Number, "open"); err != nil {
t.Fatalf("SetIssueState reopen: %v", err)
}
reopened, _ := GetIssueByNumber(database, repo.ID, published.Number)
if reopened.ClosedAt.Valid {
t.Error("ClosedAt is set after reopen, want NULL")
}
}
func TestApproveAndDeleteIssue(t *testing.T) {
database := openTestDB(t)
ownerID, _ := CreateUser(database, "josie", "hash")
repo, _ := CreateRepo(database, ownerID, "alpha", "", "public")
issue, _ := CreateIssue(database, repo.ID, "spam?", "", 0, "guest", "", true)
if err := ApproveIssue(database, repo.ID, issue.Number); err != nil {
t.Fatalf("ApproveIssue: %v", err)
}
got, _ := GetIssueByNumber(database, repo.ID, issue.Number)
if got.Pending {
t.Error("issue still pending after approve")
}
if err := DeleteIssue(database, repo.ID, issue.Number); err != nil {
t.Fatalf("DeleteIssue: %v", err)
}
if _, err := GetIssueByNumber(database, repo.ID, issue.Number); !errors.Is(err, ErrNotFound) {
t.Errorf("GetIssueByNumber after delete err = %v, want ErrNotFound", err)
}
}
func TestCommentsModerationAndCount(t *testing.T) {
database := openTestDB(t)
ownerID, _ := CreateUser(database, "josie", "hash")
repo, _ := CreateRepo(database, ownerID, "alpha", "", "public")
issue, _ := CreateIssue(database, repo.ID, "topic", "", ownerID, "josie", "", false)
ownerComment, _ := CreateComment(database, issue.ID, "owner reply", ownerID, "josie", "", false)
guestComment, _ := CreateComment(database, issue.ID, "guest reply", 0, "guest", "guest@example.com", true)
public, err := ListComments(database, issue.ID, false)
if err != nil {
t.Fatalf("ListComments public: %v", err)
}
if len(public) != 1 || public[0].ID != ownerComment.ID {
t.Errorf("public comments = %+v, want only the owner comment", public)
}
owner, err := ListComments(database, issue.ID, true)
if err != nil {
t.Fatalf("ListComments owner: %v", err)
}
if len(owner) != 2 {
t.Errorf("owner comments = %d, want 2", len(owner))
}
pending, err := CountPending(database, repo.ID)
if err != nil {
t.Fatalf("CountPending: %v", err)
}
if pending != 1 {
t.Errorf("CountPending = %d, want 1", pending)
}
if err := ApproveComment(database, issue.ID, guestComment.ID); err != nil {
t.Fatalf("ApproveComment: %v", err)
}
if pending, _ = CountPending(database, repo.ID); pending != 0 {
t.Errorf("CountPending after approve = %d, want 0", pending)
}
if err := DeleteComment(database, issue.ID, guestComment.ID); err != nil {
t.Fatalf("DeleteComment: %v", err)
}
if _, err := getComment(database, issueCommentColumns+`id = ?`, guestComment.ID); !errors.Is(err, ErrNotFound) {
t.Errorf("comment after delete err = %v, want ErrNotFound", err)
}
if err := ApproveComment(database, issue.ID+999, ownerComment.ID); !errors.Is(err, ErrNotFound) {
t.Errorf("ApproveComment wrong issue err = %v, want ErrNotFound", err)
}
}
func TestDeleteIssueCascadesComments(t *testing.T) {
database := openTestDB(t)
ownerID, _ := CreateUser(database, "josie", "hash")
repo, _ := CreateRepo(database, ownerID, "alpha", "", "public")
issue, _ := CreateIssue(database, repo.ID, "topic", "", ownerID, "josie", "", false)
if _, err := CreateComment(database, issue.ID, "reply", ownerID, "josie", "", false); err != nil {
t.Fatalf("CreateComment: %v", err)
}
if err := DeleteIssue(database, repo.ID, issue.Number); err != nil {
t.Fatalf("DeleteIssue: %v", err)
}
comments, err := ListComments(database, issue.ID, true)
if err != nil {
t.Fatalf("ListComments: %v", err)
}
if len(comments) != 0 {
t.Errorf("comments after issue delete = %d, want 0 (cascade)", len(comments))
}
}