agent build

This commit is contained in:
2026-08-28 12:21:44 +05:30
parent b6f8655909
commit f7df96c973
138 changed files with 24164 additions and 207 deletions

View File

@@ -222,11 +222,16 @@ func TestListEveryResource(t *testing.T) {
}
}
// Assignments are empty by design in the source dataset. An empty collection is
// 200 with an empty array, never a 404. api-contract.md §8.
// An empty result is 200 with an empty array, never a 404. api-contract.md §8.
//
// Asked as a filter that matches nothing, rather than as a collection that
// happens to be empty. This used to read /assignments on the strength of the
// fixture shipping none, so seeding a single assignment broke a test about
// status codes. The contract is about the empty result, not about which
// collection is empty this week.
func TestEmptyCollectionIs200(t *testing.T) {
a := newAPI(t)
r := a.do("GET", "/api/v1/assignments", nil)
r := a.do("GET", "/api/v1/assignments?status=cancelled", nil)
if r.code != http.StatusOK {
t.Fatalf("code = %d, want 200", r.code)
}
@@ -473,12 +478,16 @@ func TestFilterEquality(t *testing.T) {
// `Array.isArray(want) ? want.includes(got)`. api-contract.md §6.
func TestFilterArrayMeansIN(t *testing.T) {
a := newAPI(t)
recs := a.do("GET", "/api/v1/job-applications?status=hired&status=interview", nil).records(t)
// `assigned` is asked for deliberately: the fixture now carries one, and this
// filter is literal — it matches the stored value, not the product's rule
// that an assigned candidate also counts as hired.
recs := a.do("GET",
"/api/v1/job-applications?status=hired&status=interview&status=assigned", nil).records(t)
if len(recs) != 8 {
t.Errorf("hired+interview = %d, want 8 (3 hired, 5 interview)", len(recs))
t.Errorf("hired+interview+assigned = %d, want 8 (2 hired, 5 interview, 1 assigned)", len(recs))
}
for _, r := range recs {
if s := r["status"].(string); s != "hired" && s != "interview" {
if s := r["status"].(string); s != "hired" && s != "interview" && s != "assigned" {
t.Errorf("membership filter leaked status %q", s)
}
}
@@ -1112,3 +1121,90 @@ func keysOf(m map[string]any) []string {
sort.Strings(out)
return out
}
// The build identifier has to be reachable, or "did my deploy land?" has no
// answer. It was reported nowhere: the Dockerfile declared a VERSION arg,
// compose passed it, and it reached no linker flag — so every deployment
// described itself as nothing at all.
//
// Under /api/v1 rather than on /health on purpose: /health is public and
// deliberately withholds its detail from the internet, and a build identifier
// tells an unauthenticated reader exactly which source to go and read.
func TestVersionEndpointReportsTheBuild(t *testing.T) {
a := newAPI(t)
r := a.do("GET", "/api/v1/version", nil)
if r.code != http.StatusOK {
t.Fatalf("code = %d, want 200", r.code)
}
data, _ := r.body["data"].(map[string]any)
if data == nil {
t.Fatalf("no data envelope: %v", r.body)
}
if v, _ := data["version"].(string); v == "" {
t.Errorf("version is empty; an unstamped build should still say \"dev\": %v", data)
}
if e, _ := data["env"].(string); e == "" {
t.Errorf("env is empty: %v", data)
}
if n, _ := data["endpoints"].(float64); n < 1 {
t.Errorf("endpoints = %v, want the served route count", data["endpoints"])
}
}
// It is behind the session like every other /api/v1 route.
func TestVersionEndpointNeedsASession(t *testing.T) {
a := newAPI(t)
r := a.doAnon("GET", "/api/v1/version", nil)
if r.code != http.StatusUnauthorized && r.code != http.StatusForbidden {
t.Errorf("anonymous GET /api/v1/version = %d, want 401/403", r.code)
}
}
// An agent author picks capabilities from the real tool set, not a copy of it
// kept in the frontend. A second list would drift, and the failure is silent:
// the author picks a tool that no longer exists and gets an agent that quietly
// cannot do the thing they picked.
func TestToolsCatalogueIsServed(t *testing.T) {
a := newAPI(t)
r := a.do("GET", "/api/v1/tools", nil)
if r.code != http.StatusOK {
t.Fatalf("code = %d, want 200", r.code)
}
list, _ := r.body["data"].([]any)
if len(list) == 0 {
t.Fatalf("no tools served: %v", r.body)
}
seenWrite := false
for _, raw := range list {
tool, _ := raw.(map[string]any)
name, _ := tool["name"].(string)
desc, _ := tool["description"].(string)
effect, _ := tool["effect"].(string)
if name == "" || desc == "" {
t.Errorf("a tool has no name or description: %v", tool)
}
if effect != "read" && effect != "write" {
t.Errorf("%s has effect %q, want read or write", name, effect)
}
if effect == "write" {
seenWrite = true
// An author must be able to see that this one proposes changes.
if confirm, _ := tool["requiresConfirmation"].(bool); !confirm {
t.Errorf("%s writes but does not report requiring confirmation", name)
}
}
}
if !seenWrite {
t.Error("no write tool in the catalogue; the effect distinction is untested")
}
}
func TestToolsCatalogueNeedsASession(t *testing.T) {
a := newAPI(t)
if r := a.doAnon("GET", "/api/v1/tools", nil); r.code != http.StatusUnauthorized && r.code != http.StatusForbidden {
t.Errorf("anonymous GET /api/v1/tools = %d, want 401/403", r.code)
}
}