test(19.3-05): build tags, S3 tag guard, DiskExtension and public_url
- manifest_tags_test.go: build.tags load, trim, dedupe and validation errors naming build.tags; SaveManifest keeps tags and plugin order; goBuildArgs puts one -tags before -o - s3_test.go: s3:// refused without summercms_s3 naming the tag, opens with it (both build modes, any scheme case) - diskext_test.go: RESEARCH probe rows and table consistency - url_test.go: public_url base joining, File.URL and thumbnails, malformed values refused naming the key - store_test.go: Postgres regression table through Store - gate: attach names in both tag modes, Store regression in docker
This commit is contained in:
189
internal/build/manifest_tags_test.go
Normal file
189
internal/build/manifest_tags_test.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Unit tests (plan 05 of phase 19.3) for summer.yaml build.tags (D-12).
|
||||
|
||||
const tagsManifestHead = "module: git.golem15.com/acme/acme\n" +
|
||||
"binary: acme\n" +
|
||||
"plugins:\n" +
|
||||
" - id: acme.blog\n" +
|
||||
" module: git.golem15.com/acme/acme/plugins/blog\n"
|
||||
|
||||
func writeManifest(t *testing.T, body string) string {
|
||||
t.Helper()
|
||||
path := filepath.Join(t.TempDir(), manifestName)
|
||||
if err := os.WriteFile(path, []byte(body), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func TestManifestBuildTags(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
body string
|
||||
want []string
|
||||
}{
|
||||
{"no build block", tagsManifestHead, nil},
|
||||
{"empty build block", tagsManifestHead + "build: {}\n", nil},
|
||||
{"null tags", tagsManifestHead + "build:\n tags:\n", nil},
|
||||
{"empty list", tagsManifestHead + "build:\n tags: []\n", nil},
|
||||
{"one tag", tagsManifestHead + "build:\n tags: [summercms_s3]\n", []string{"summercms_s3"}},
|
||||
{"trimmed and deduplicated in order", tagsManifestHead + "build:\n tags:\n - \" netgo \"\n - summercms_s3\n - netgo\n - \"summercms_s3\\t\"\n - osusergo\n", []string{"netgo", "summercms_s3", "osusergo"}},
|
||||
{"digits and underscores", tagsManifestHead + "build:\n tags: [go1_x, \"123\"]\n", []string{"go1_x", "123"}},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
m, err := LoadManifest(writeManifest(t, tc.body))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !slices.Equal(m.Tags, tc.want) {
|
||||
t.Fatalf("Tags = %q, want %q", m.Tags, tc.want)
|
||||
}
|
||||
if len(m.Plugins) != 1 || m.Plugins[0].ID != "acme.blog" {
|
||||
t.Fatalf("plugins = %+v", m.Plugins)
|
||||
}
|
||||
})
|
||||
}
|
||||
for _, tag := range []string{"summercms_s3", "a", "0", "x_1_y"} {
|
||||
if err := ValidateBuildTag(tag); err != nil {
|
||||
t.Errorf("ValidateBuildTag(%q) = %v", tag, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestManifestBuildTagsInvalid(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
yaml string
|
||||
want string
|
||||
}{
|
||||
{"dash", "build:\n tags: [summercms-s3]\n", "build.tags[0]"},
|
||||
{"space", "build:\n tags: [netgo, \"summercms s3\"]\n", "build.tags[1]"},
|
||||
{"upper case", "build:\n tags: [SummerCMS_S3]\n", "build.tags[0]"},
|
||||
{"flag injection", "build:\n tags: [\"x -ldflags=-X\"]\n", "build.tags[0]"},
|
||||
{"comma", "build:\n tags: [\"a,b\"]\n", "build.tags[0]"},
|
||||
{"empty string", "build:\n tags: [\" \"]\n", "build.tags[0]"},
|
||||
{"non-string entry", "build:\n tags: [42]\n", "build.tags[0]"},
|
||||
{"non-list tags", "build:\n tags: summercms_s3\n", "build.tags"},
|
||||
{"mapping tags", "build:\n tags:\n s3: true\n", "build.tags"},
|
||||
{"non-mapping build", "build: [summercms_s3]\n", "build.tags"},
|
||||
{"scalar build", "build: summercms_s3\n", "build.tags"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := LoadManifest(writeManifest(t, tagsManifestHead+tc.yaml))
|
||||
if err == nil || !strings.Contains(err.Error(), tc.want) {
|
||||
t.Fatalf("err = %v, want one naming %s", err, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
for _, bad := range []string{"", "A", "a-b", "a b", "a.b", "-tags", "a,b", "ä"} {
|
||||
if err := ValidateBuildTag(bad); err == nil {
|
||||
t.Errorf("ValidateBuildTag(%q) accepted", bad)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveManifestKeepsBuildTags(t *testing.T) {
|
||||
path := writeManifest(t, tagsManifestHead+
|
||||
" - id: acme.shop\n"+
|
||||
" module: git.golem15.com/acme/acme/plugins/shop\n"+
|
||||
"build:\n tags:\n - summercms_s3\n - netgo\n")
|
||||
m, err := LoadManifest(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// What plugin:add does: append a plugin and save.
|
||||
m.Plugins = append(m.Plugins, Plugin{ID: "acme.faq", Module: "git.golem15.com/acme/acme/plugins/faq"})
|
||||
if err := SaveManifest(path, m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
again, err := LoadManifest(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var ids []string
|
||||
for _, p := range again.Plugins {
|
||||
ids = append(ids, p.ID)
|
||||
}
|
||||
if !slices.Equal(ids, []string{"acme.blog", "acme.shop", "acme.faq"}) {
|
||||
t.Fatalf("plugin order = %q", ids)
|
||||
}
|
||||
if !slices.Equal(again.Tags, []string{"summercms_s3", "netgo"}) {
|
||||
t.Fatalf("Tags = %q after the round trip", again.Tags)
|
||||
}
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.HasSuffix(string(raw), "build:\n tags:\n - summercms_s3\n - netgo\n") {
|
||||
t.Fatalf("build block is not written after the plugins:\n%s", raw)
|
||||
}
|
||||
|
||||
// No tags: no build block at all.
|
||||
m.Tags = nil
|
||||
if err := SaveManifest(path, m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raw, _ = os.ReadFile(path)
|
||||
if strings.Contains(string(raw), "build:") {
|
||||
t.Fatalf("empty tags wrote a build block:\n%s", raw)
|
||||
}
|
||||
m.Tags = []string{}
|
||||
if err := SaveManifest(path, m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raw, _ = os.ReadFile(path)
|
||||
if strings.Contains(string(raw), "build:") {
|
||||
t.Fatalf("an empty tag list wrote a build block:\n%s", raw)
|
||||
}
|
||||
|
||||
// SaveManifest refuses an invalid tag and leaves the file alone.
|
||||
before, _ := os.ReadFile(path)
|
||||
m.Tags = []string{"ok", "not-ok"}
|
||||
if err := SaveManifest(path, m); err == nil || !strings.Contains(err.Error(), "build.tags[1]") {
|
||||
t.Fatalf("SaveManifest with an invalid tag: %v", err)
|
||||
}
|
||||
after, _ := os.ReadFile(path)
|
||||
if string(after) != string(before) {
|
||||
t.Fatal("SaveManifest wrote a file with an invalid tag")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoBuildArgs(t *testing.T) {
|
||||
if got := goBuildArgs("bin/acme", nil); !slices.Equal(got, []string{"build", "-o", "bin/acme", "."}) {
|
||||
t.Fatalf("no tags = %q", got)
|
||||
}
|
||||
if got := goBuildArgs("bin/acme", []string{}); slices.ContainsFunc(got, func(a string) bool { return strings.HasPrefix(a, "-tags") }) {
|
||||
t.Fatalf("empty tags = %q", got)
|
||||
}
|
||||
got := goBuildArgs("bin/acme", []string{"a", "b"})
|
||||
if !slices.Equal(got, []string{"build", "-tags=a,b", "-o", "bin/acme", "."}) {
|
||||
t.Fatalf("tags = %q", got)
|
||||
}
|
||||
n := 0
|
||||
for _, a := range got {
|
||||
if strings.HasPrefix(a, "-tags") {
|
||||
n++
|
||||
}
|
||||
}
|
||||
if n != 1 {
|
||||
t.Fatalf("%d -tags arguments", n)
|
||||
}
|
||||
|
||||
// The tags of a loaded manifest reach the arguments unchanged.
|
||||
m, err := LoadManifest(writeManifest(t, tagsManifestHead+"build:\n tags: [summercms_s3, netgo]\n"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := goBuildArgs("bin/"+m.Binary, m.Tags); !slices.Equal(got, []string{"build", "-tags=summercms_s3,netgo", "-o", "bin/acme", "."}) {
|
||||
t.Fatalf("manifest tags = %q", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user