package server import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "path/filepath" "testing" "time" "task/internal/sqlite" "task/internal/ticket" ) func TestProjectReportFilters(t *testing.T) { db, err := sqlite.Open(filepath.Join(t.TempDir(), "t.db"), []byte("secret")) if err != nil { t.Fatal(err) } defer db.Close() if _, err := db.CreateUser("admin", "pass", true); err != nil { t.Fatal(err) } srv := New(db) handler := srv.Handler() loginBody, _ := json.Marshal(map[string]string{"username": "admin", "password": "pass"}) req := httptest.NewRequest(http.MethodPost, "/api/v1/auth/login", bytes.NewReader(loginBody)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() handler.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("login %d %s", rec.Code, rec.Body.String()) } cookie := rec.Result().Cookies()[0] createBody, _ := json.Marshal(map[string]string{"name": "Demo"}) req = httptest.NewRequest(http.MethodPost, "/api/v1/projects", bytes.NewReader(createBody)) req.Header.Set("Content-Type", "application/json") req.AddCookie(cookie) rec = httptest.NewRecorder() handler.ServeHTTP(rec, req) var proj map[string]any _ = json.Unmarshal(rec.Body.Bytes(), &proj) pid, _ := proj["id"].(string) ticketBody, _ := json.Marshal(map[string]string{"title": "Work"}) req = httptest.NewRequest(http.MethodPost, "/api/v1/projects/"+pid+"/tickets", bytes.NewReader(ticketBody)) req.Header.Set("Content-Type", "application/json") req.AddCookie(cookie) rec = httptest.NewRecorder() handler.ServeHTTP(rec, req) var tk map[string]any _ = json.Unmarshal(rec.Body.Bytes(), &tk) tid, _ := tk["id"].(string) day := time.Date(2026, 10, 18, 14, 0, 0, 0, time.Local) _, err = srv.Times.Create(ticket.TimeEntry{ TicketID: tid, ProjectID: pid, Start: day, End: day.Add(45 * time.Minute), Duration: 45 * time.Minute, Source: ticket.SourceManual, Note: "filtered", }) if err != nil { t.Fatal(err) } other := time.Date(2026, 9, 1, 10, 0, 0, 0, time.Local) _, err = srv.Times.Create(ticket.TimeEntry{ TicketID: tid, ProjectID: pid, Start: other, End: other.Add(time.Hour), Duration: time.Hour, Source: ticket.SourceManual, }) if err != nil { t.Fatal(err) } url := "/api/v1/projects/" + pid + "/report?groupBy=ticket&from=2026-10-18&to=2026-10-18&ticketId=" + tid req = httptest.NewRequest(http.MethodGet, url, nil) req.AddCookie(cookie) rec = httptest.NewRecorder() handler.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("report %d %s", rec.Code, rec.Body.String()) } var out struct { EntryCount int `json:"entryCount"` TotalDurationNs int64 `json:"totalDurationNs"` Entries []ticket.TimeEntry `json:"entries"` } if err := json.Unmarshal(rec.Body.Bytes(), &out); err != nil { t.Fatal(err) } if out.EntryCount != 1 || len(out.Entries) != 1 { t.Fatalf("want 1 entry, got count=%d entries=%d", out.EntryCount, len(out.Entries)) } if out.TotalDurationNs != int64(45*time.Minute) { t.Fatalf("total=%d", out.TotalDurationNs) } if out.Entries[0].Note != "filtered" { t.Fatalf("note=%q", out.Entries[0].Note) } }