package main import ( "os" "path/filepath" "testing" ) // TestCopyDirContentsCreatesDestination pins the bug that broke copying a QSL // design between profiles. // // copyDirContents wrote straight into dst without creating it, so the first // os.Create failed with ENOENT. The caller was written as "ignore // os.IsNotExist", meaning "the source has no assets, that is fine" — and ENOENT // from the failed write matched it exactly. Nothing was copied, no error was // reported, and the operator saw "copied design is incomplete: hero photo file // … not found" from the validation two lines later. func TestCopyDirContentsCreatesDestination(t *testing.T) { base := t.TempDir() src := filepath.Join(base, "src") if err := os.MkdirAll(filepath.Join(src, "sub"), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(src, "img_25c06bda.jpg"), []byte("hero"), 0o644); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(src, "sub", "back.png"), []byte("back"), 0o644); err != nil { t.Fatal(err) } // dst does not exist — the case of every first copy. dst := filepath.Join(base, "dst") if err := copyDirContents(src, dst); err != nil { t.Fatalf("copyDirContents: %v", err) } for name, want := range map[string]string{ "img_25c06bda.jpg": "hero", "sub/back.png": "back", } { got, err := os.ReadFile(filepath.Join(dst, filepath.FromSlash(name))) if err != nil { t.Errorf("%s: %v", name, err) continue } if string(got) != want { t.Errorf("%s = %q, want %q", name, got, want) } } } // A missing SOURCE is still an error from copyDirContents itself — it is the // caller that decides whether a design with no pictures is acceptable, and it // can only decide that if this does not quietly succeed. func TestCopyDirContentsMissingSource(t *testing.T) { base := t.TempDir() if err := copyDirContents(filepath.Join(base, "nope"), filepath.Join(base, "dst")); err == nil { t.Fatal("copying a missing source returned no error") } else if !os.IsNotExist(err) { t.Fatalf("want a not-exist error, got %v", err) } }