Files
2026-08-09 09:33:47 -04:00

111 lines
3.7 KiB
Go

package web
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/MeshTender/MeshTender/internal/buildinfo"
)
func testBuild() buildinfo.Info {
return buildinfo.Info{
Commit: "c0ffeec0ffeec0ffeec0ffeec0ffeec0ffeec0ff", CommitTime: "2026-08-06T17:47:49-04:00",
Go: "go1.26.5", OS: "linux", Arch: "amd64",
ExecutableSHA256: strings.Repeat("a", 64),
ImageDigest: "sha256:" + strings.Repeat("b", 64),
}
}
// TestVersionJSONReportsTheBuild is the contract an auditor scripts against:
// the endpoint answers with the build info as JSON, unauthenticated.
func TestVersionJSONReportsTheBuild(t *testing.T) {
t.Parallel()
e := &Env{Build: testBuild()}
rec := httptest.NewRecorder()
e.VersionJSON(rec, httptest.NewRequest(http.MethodGet, VersionPath, nil))
res := rec.Result()
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
t.Fatalf("status = %d, want 200", res.StatusCode)
}
if ct := res.Header.Get("Content-Type"); !strings.HasPrefix(ct, "application/json") {
t.Errorf("Content-Type = %q, want JSON", ct)
}
var got versionResponse
if err := json.NewDecoder(res.Body).Decode(&got); err != nil {
t.Fatalf("decode: %v", err)
}
if got.Info != testBuild() {
t.Errorf("reported %+v, want %+v", got.Info, testBuild())
}
if got.Source != SourceURL || got.License != License {
t.Errorf("reported source/license %q/%q, want %q/%q", got.Source, got.License, SourceURL, License)
}
}
// TestVersionJSONAlwaysOffersSource: the source location and license are the
// AGPL section 13 offer in machine-readable form, so unlike the build stamps they
// must be present on every build — including a from-source run with no VCS
// stamps, where an omitempty slip would leave a verifier with nothing to fetch.
func TestVersionJSONAlwaysOffersSource(t *testing.T) {
t.Parallel()
e := &Env{Build: buildinfo.Info{Go: "go1.26.5", OS: "darwin", Arch: "arm64"}}
rec := httptest.NewRecorder()
e.VersionJSON(rec, httptest.NewRequest(http.MethodGet, VersionPath, nil))
body := rec.Body.String()
for _, want := range []string{`"source":"` + SourceURL + `"`, `"license":"` + License + `"`} {
if !strings.Contains(body, want) {
t.Errorf("body missing %s, got %s", want, body)
}
}
}
// TestVersionJSONRevalidatesCheaply: the payload is fixed for the life of the
// process, so a repeat request should be answerable with a 304 rather than
// re-sending it.
func TestVersionJSONRevalidatesCheaply(t *testing.T) {
t.Parallel()
e := &Env{Build: testBuild()}
first := httptest.NewRecorder()
e.VersionJSON(first, httptest.NewRequest(http.MethodGet, VersionPath, nil))
etag := first.Result().Header.Get("ETag")
if etag == "" {
t.Fatal("no ETag on the first response")
}
req := httptest.NewRequest(http.MethodGet, VersionPath, nil)
req.Header.Set("If-None-Match", etag)
second := httptest.NewRecorder()
e.VersionJSON(second, req)
if second.Code != http.StatusNotModified {
t.Errorf("revalidated request = %d, want 304", second.Code)
}
}
// TestVersionJSONOmitsUnstampedFields: a from-source build must not report a
// commit it doesn't have. An auditor reading "commit": "" could otherwise take
// it as a claim rather than as an absence.
func TestVersionJSONOmitsUnstampedFields(t *testing.T) {
t.Parallel()
e := &Env{Build: buildinfo.Info{Go: "go1.26.5", OS: "darwin", Arch: "arm64"}}
rec := httptest.NewRecorder()
e.VersionJSON(rec, httptest.NewRequest(http.MethodGet, VersionPath, nil))
body := rec.Body.String()
for _, k := range []string{"commit", "commitTime", "executableSHA256", "imageDigest"} {
if strings.Contains(body, `"`+k+`"`) {
t.Errorf("unstamped %s should be omitted, got %s", k, body)
}
}
if !strings.Contains(body, `"go":"go1.26.5"`) {
t.Errorf("toolchain should always be reported, got %s", body)
}
}