Files
projax/web/gitea_test.go
mAi 1ffbfc6e69 feat(phase 2.d gitea): read-only issue ingest on items with gitea-repo links
gitea package (new): minimal client mirroring caldav's structure
- client.go: token auth, 5s timeout, ErrNotFound
- issues.go: ListIssues(owner, repo, opts) hitting
  /repos/{o}/{r}/issues?type=issues&state=…&since=…, ParseRepoRef,
  RepoHTMLURL. PullRequest-flagged rows dropped server- and client-side.
- httptest stubs covering parse, 404, ParseRepoRef variants.

web wiring:
- Server.Gitea optional GiteaDeps (Client + in-memory 3-min TTL cache
  keyed by owner/repo|state).
- detailIssues iterates every gitea-repo link, sums open issues, captures
  last-30d closed (≤20) into a disclosure. Per-repo failures surface as
  banner; one missing repo never blanks the section.
- relativeTime renders "Nm/h/d ago" / "yesterday" / fallback date.

Templates:
- issues_section.tmpl: per-repo block, header "Issues (n) + ↗ Gitea repo",
  rows with #N · title · labels · milestone · assignees · updated.
  Titles open in new tab.
- detail.tmpl: include the partial when Gitea is on and issues != nil.
- CSS: matches the Tasks section visual language.

main.go: GITEA_URL gates the integration (off when unset). GITEA_URL set
but GITEA_TOKEN missing → refuse to start.

deploy/dokploy.yaml: GITEA_URL env + GITEA_TOKEN secret added.

docs/design.md: new §6 mirroring §5's structure (link model, listing
semantics, caching, env contract, parked items).
2026-05-15 17:27:01 +02:00

67 lines
1.6 KiB
Go

package web
import (
"testing"
"time"
"github.com/m/projax/gitea"
)
func TestIssueCacheTTL(t *testing.T) {
c := newIssueCache(50 * time.Millisecond)
c.set("k", []gitea.Issue{{Number: 1, Title: "a"}})
if got, ok := c.get("k"); !ok || len(got) != 1 {
t.Fatalf("immediate get: ok=%v got=%v", ok, got)
}
time.Sleep(80 * time.Millisecond)
if _, ok := c.get("k"); ok {
t.Errorf("expected miss after TTL, got hit")
}
}
func TestRelativeTime(t *testing.T) {
now := time.Date(2026, 5, 15, 12, 0, 0, 0, time.UTC)
for _, tc := range []struct {
offset time.Duration
want string
}{
{-30 * time.Second, "just now"},
{-5 * time.Minute, "5m ago"},
{-3 * time.Hour, "3h ago"},
{-30 * time.Hour, "yesterday"},
{-5 * 24 * time.Hour, "5d ago"},
} {
got := relativeTime(now, now.Add(tc.offset))
if got != tc.want {
t.Errorf("offset=%v got %q want %q", tc.offset, got, tc.want)
}
}
}
func TestToViewPreservesFields(t *testing.T) {
in := []gitea.Issue{
{
Number: 7,
Title: "x",
State: "open",
Labels: []string{"bug"},
Assignees: []string{"a"},
Milestone: "M",
HTMLURL: "https://example/7",
UpdatedAt: time.Date(2026, 5, 14, 12, 0, 0, 0, time.UTC),
},
}
now := time.Date(2026, 5, 15, 12, 0, 0, 0, time.UTC)
got := toView(in, now)
if len(got) != 1 {
t.Fatalf("expected 1, got %d", len(got))
}
v := got[0]
if v.Number != 7 || v.Title != "x" || v.HTMLURL != "https://example/7" {
t.Errorf("field mismatch: %+v", v)
}
if v.UpdatedRel != "yesterday" {
t.Errorf("UpdatedRel = %q, want yesterday", v.UpdatedRel)
}
}