Files
DoGaMa-serv/internal/catalog/local_test.go
T
codex 75d5504729
CI / validate (pull_request) Canceled after 3m32s
refactor(modules): load wasm modules from templates
2026-08-25 17:52:28 +02:00

197 lines
6.1 KiB
Go

package catalog_test
import (
"os"
"path/filepath"
"strings"
"testing"
catalogdata "git.zaynet.fr/DoGaMa/DoGaMa-serv/catalog"
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/catalog"
)
func TestInitializeOfficialAndScanDirPreservesLocalFiles(t *testing.T) {
root := t.TempDir()
if err := catalog.InitializeOfficial(root, catalogdata.Files); err != nil {
t.Fatal(err)
}
first, err := catalog.ScanDir(root)
if err != nil {
t.Fatal(err)
}
if len(first.Valid) == 0 || first.Found != len(first.Valid) || len(first.Errors) != 0 {
t.Fatalf("initial catalog scan = %#v", first)
}
path := filepath.Join(root, first.Valid[0].AssetRoot, "template.yaml")
if err := os.WriteFile(path, []byte("local customization"), 0o644); err != nil {
t.Fatal(err)
}
if err := catalog.InitializeOfficial(root, catalogdata.Files); err != nil {
t.Fatal(err)
}
body, err := os.ReadFile(path)
if err != nil || string(body) != "local customization" {
t.Fatalf("local template was overwritten: %q, %v", body, err)
}
}
func TestScanDirKeepsValidTemplatesWhenOneIsInvalid(t *testing.T) {
root := t.TempDir()
if err := catalog.InitializeOfficial(root, catalogdata.Files); err != nil {
t.Fatal(err)
}
initial, err := catalog.ScanDir(root)
if err != nil || len(initial.Valid) == 0 {
t.Fatalf("initial scan = %#v, %v", initial, err)
}
broken := strings.Replace(initial.Valid[0].CanonicalYAML, `"image": "`+initial.Valid[0].Template.Game.Artwork.Image+`"`, `"image": "../invalid.jpg"`, 1)
if err := os.Mkdir(filepath.Join(root, "broken"), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(root, "broken", "template.yaml"), []byte(broken), 0o644); err != nil {
t.Fatal(err)
}
result, err := catalog.ScanDir(root)
if err != nil {
t.Fatal(err)
}
if result.Found != initial.Found+1 || len(result.Valid) != len(initial.Valid) || len(result.Errors) != 1 {
t.Fatalf("scan with invalid template = %#v", result)
}
if result.Errors[0].Template != "broken" || !strings.Contains(result.Errors[0].Message, "/game/artwork/image") {
t.Fatalf("error = %#v", result.Errors)
}
}
func TestScanDirAcceptsLocallyModifiedReferencedAsset(t *testing.T) {
root := t.TempDir()
if err := catalog.InitializeOfficial(root, catalogdata.Files); err != nil {
t.Fatal(err)
}
before, err := catalog.ScanDir(root)
if err != nil {
t.Fatal(err)
}
for _, snapshot := range before.Valid {
if len(snapshot.Template.Container.Assets) == 0 {
continue
}
asset := snapshot.Template.Container.Assets[0]
assetPath := filepath.Join(root, filepath.FromSlash(snapshot.AssetRoot), filepath.FromSlash(asset.Source))
if err := os.WriteFile(assetPath, []byte("locally maintained asset\n"), 0o755); err != nil {
t.Fatal(err)
}
templatePath := filepath.Join(root, filepath.FromSlash(snapshot.AssetRoot), "template.yaml")
body, err := os.ReadFile(templatePath)
if err != nil {
t.Fatal(err)
}
legacy := strings.Replace(string(body), " destination: "+asset.Destination+"\n", " destination: "+asset.Destination+"\n sha256: obsolete-checksum\n", 1)
if err := os.WriteFile(templatePath, []byte(legacy), 0o644); err != nil {
t.Fatal(err)
}
after, scanErr := catalog.ScanDir(root)
if scanErr != nil || len(after.Errors) != 0 || len(after.Valid) != len(before.Valid) {
t.Fatalf("scan after modifying asset %q = %#v, %v", assetPath, after, scanErr)
}
return
}
t.Skip("embedded catalog has no container assets")
}
func TestScanDirLoadsTemplateLocalModuleAndAcceptsChanges(t *testing.T) {
root := t.TempDir()
if err := catalog.InitializeOfficial(root, catalogdata.Files); err != nil {
t.Fatal(err)
}
first, err := catalog.ScanDir(root)
if err != nil || len(first.Valid) == 0 {
t.Fatalf("initial scan = %#v, %v", first, err)
}
var snapshot catalog.Snapshot
for _, candidate := range first.Valid {
if candidate.Template.Module != nil {
snapshot = candidate
break
}
}
if snapshot.Template.Module == nil || len(snapshot.ModuleFiles) == 0 {
t.Fatal("template-local module was not discovered")
}
wasmPath := filepath.Join(root, snapshot.AssetRoot, "module", "module.wasm")
body, err := os.ReadFile(wasmPath)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(wasmPath, append(body, 0), 0o644); err != nil {
t.Fatal(err)
}
second, err := catalog.ScanDir(root)
if err != nil || len(second.Errors) != 0 {
t.Fatalf("modified local module scan = %#v, %v", second, err)
}
var updated catalog.Snapshot
for _, candidate := range second.Valid {
if candidate.Template.ID == snapshot.Template.ID {
updated = candidate
break
}
}
if updated.Digest == snapshot.Digest {
t.Fatal("module change did not affect template snapshot digest")
}
}
func TestScanDirAcceptsLocalTemplateWithoutModule(t *testing.T) {
root := t.TempDir()
if err := catalog.InitializeOfficial(root, catalogdata.Files); err != nil {
t.Fatal(err)
}
result, err := catalog.ScanDir(root)
if err != nil {
t.Fatal(err)
}
for _, snapshot := range result.Valid {
if snapshot.Template.Module == nil {
return
}
}
t.Fatal("a template without an optional module was not accepted")
}
func TestScanDirRejectsMissingAndEscapingLocalModule(t *testing.T) {
for _, modulePath := range []string{"module/missing.yaml", "../outside.wasm", "/outside.wasm"} {
t.Run(modulePath, func(t *testing.T) {
root := t.TempDir()
if err := catalog.InitializeOfficial(root, catalogdata.Files); err != nil {
t.Fatal(err)
}
initial, err := catalog.ScanDir(root)
if err != nil {
t.Fatal(err)
}
var snapshot catalog.Snapshot
for _, candidate := range initial.Valid {
if candidate.Template.Module != nil {
snapshot = candidate
break
}
}
path := filepath.Join(root, snapshot.AssetRoot, "template.yaml")
body, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(strings.Replace(string(body), snapshot.Template.Module.Path, modulePath, 1)), 0o644); err != nil {
t.Fatal(err)
}
result, err := catalog.ScanDir(root)
if err != nil || len(result.Errors) != 1 || result.Errors[0].Template != snapshot.AssetRoot {
t.Fatalf("unsafe local module scan = %#v, %v", result, err)
}
})
}
}