Files
gitea/models/project/column_list_test.go
T
Oleks 1cd81ff925 feat(api): state filter + populated num_issues on project columns
Adds two improvements to the user/org/repo project-board REST API:

* state filter on column-issues endpoints (issue #4)
  GET /api/v1/{users,orgs}/{name}/-/projects/{id}/columns/{col}/issues
  GET /api/v1/repos/{owner}/{repo}/projects/{id}/columns/{col}/issues
  Now accept ?state=open|closed|all (default open), matching the convention
  on the project-list endpoint and on /repos/.../issues. Applied at the
  IssuesOptions layer so all three scopes inherit the filter.

* populated num_issues / num_open_issues / num_closed_issues on column-list
  (issue #5)
  ColumnList.LoadIssueCounts runs two grouped queries against project_issue
  joined with issue (one open, one closed). All three List*Columns handlers
  call it before converting, so num_issues stops being null and consumers
  can render a kanban summary in a single round trip instead of N+1.

Tests:
* unit: empty-input fast path on ColumnList.LoadIssueCounts.
* integration: extended testAPIListProjectColumnIssues / -User / -Org to
  close an issue, then verify default=open hides it, state=closed and
  state=all return it, and the column-list response carries the correct
  open/closed/total split.

Closes #4, closes #5
2026-05-15 21:54:35 +03:00

76 lines
2.4 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package project
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
)
func TestProjectColumns(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
t.Run("CountProjectColumns", testCountProjectColumns)
t.Run("GetProjectColumns", testGetProjectColumns)
t.Run("GetColumnsByIDs", testGetColumnsByIDs)
t.Run("LoadIssueCountsEmpty", testLoadIssueCountsEmpty)
}
func testCountProjectColumns(t *testing.T) {
project, err := GetProjectByID(t.Context(), 1)
assert.NoError(t, err)
count, err := CountProjectColumns(t.Context(), project.ID)
assert.NoError(t, err)
assert.EqualValues(t, 3, count)
}
func testGetProjectColumns(t *testing.T) {
project, err := GetProjectByID(t.Context(), 1)
assert.NoError(t, err)
// Page 1, limit 2 — returns first 2 columns
page1, err := GetProjectColumns(t.Context(), project.ID, db.ListOptions{Page: 1, PageSize: 2})
assert.NoError(t, err)
assert.Len(t, page1, 2)
// Page 2, limit 2 — returns remaining column
page2, err := GetProjectColumns(t.Context(), project.ID, db.ListOptions{Page: 2, PageSize: 2})
assert.NoError(t, err)
assert.Len(t, page2, 1)
// Page 1 and page 2 together cover all columns with no overlap
allIDs := make(map[int64]bool)
for _, c := range append(page1, page2...) {
assert.False(t, allIDs[c.ID], "duplicate column ID %d across pages", c.ID)
allIDs[c.ID] = true
}
assert.Len(t, allIDs, 3)
}
func testLoadIssueCountsEmpty(t *testing.T) {
// Empty input is a fast path — must not touch the database and must not error.
// (The full open/closed-count behavior is exercised by the integration tests
// in tests/integration/api_*_project_test.go, which can join against the issue
// table; the unit-test fixture set here intentionally excludes it.)
assert.NoError(t, ColumnList{}.LoadIssueCounts(t.Context()))
}
func testGetColumnsByIDs(t *testing.T) {
project, err := GetProjectByID(t.Context(), 1)
assert.NoError(t, err)
columns, err := GetColumnsByIDs(t.Context(), project.ID, []int64{1, 3, 4})
assert.NoError(t, err)
assert.Len(t, columns, 2)
assert.ElementsMatch(t, []int64{1, 3}, []int64{columns[0].ID, columns[1].ID})
empty, err := GetColumnsByIDs(t.Context(), project.ID, nil)
assert.NoError(t, err)
assert.Empty(t, empty)
}