package main
import (
"os"
"path/filepath"
"strings"
"testing"
)
func writeTestSource(t *testing.T, path, source string) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(source), 0o644); err != nil {
t.Fatal(err)
}
}
func TestProjectManifestAllowsMergedSources(t *testing.T) {
root := t.TempDir()
for _, directory := range []string{"vendor-a", "vendor-b", "special"} {
if err := os.Mkdir(filepath.Join(root, directory), 0o755); err != nil {
t.Fatal(err)
}
}
source := "sources vendor \"./vendor-a\"\n" +
"sources vendor \"./vendor-b\"\n" +
"source vendor.special \"./special\"\n" +
"source vendor.special \"./special\"\n"
manifest, err := parseProjectManifest(filepath.Join(root, "qk.mod"), source)
if err != nil {
t.Fatal(err)
}
if len(manifest.Mounts) != 4 {
t.Fatalf("got %d source mounts, want 4", len(manifest.Mounts))
}
}
func TestProjectManifestRejectsConflictingExactSources(t *testing.T) {
root := t.TempDir()
for _, directory := range []string{"first", "second"} {
if err := os.Mkdir(filepath.Join(root, directory), 0o755); err != nil {
t.Fatal(err)
}
}
source := "source vendor.foo \"./first\"\nsource vendor.foo \"./second\"\n"
_, err := parseProjectManifest(filepath.Join(root, "qk.mod"), source)
if err == nil || !strings.Contains(err.Error(), "conflicting source mount") {
t.Fatalf("expected conflicting source mount error, got %v", err)
}
}
func TestDiscoverSourcePackagesMergesMounts(t *testing.T) {
root := t.TempDir()
app := filepath.Join(root, "app")
library := filepath.Join(root, "libs")
first := filepath.Join(root, "vendor-a")
second := filepath.Join(root, "vendor-b")
special := filepath.Join(root, "special")
writeTestSource(t, filepath.Join(app, "main.qk"), `module main
import vendor.builtin
import vendor.alpha
import vendor.beta
import vendor.special
import vendor.special.child
let main() {}
`)
writeTestSource(t, filepath.Join(library, "vendor", "builtin", "main.qk"), "module vendor.builtin\n")
writeTestSource(t, filepath.Join(first, "alpha", "main.qk"), "module alpha\n")
writeTestSource(t, filepath.Join(second, "beta", "main.qk"), "module beta\n")
writeTestSource(t, filepath.Join(special, "main.qk"), "module special\n")
writeTestSource(t, filepath.Join(special, "child", "main.qk"), "module special.child\n")
mounts := []sourceMount{
{Kind: sourceMountCollection, Prefix: "vendor", Directory: first},
{Kind: sourceMountCollection, Prefix: "vendor", Directory: second},
{Kind: sourceMountExact, Prefix: "vendor.special", Directory: special, SemanticBase: "special"},
}
packages, _, _, _, resolutions, err := discoverSourcePackages(
".", app, "", []string{library, app}, mounts, library, false,
)
if err != nil {
t.Fatal(err)
}
got := make(map[string]bool)
for _, pkg := range packages {
got[pkg.Path] = true
}
for _, path := range []string{".", "vendor.builtin", "alpha", "beta", "special", "special.child"} {
if !got[path] {
t.Errorf("module %q was not discovered", path)
}
}
wantResolutions := map[string]string{
"vendor.builtin": "vendor.builtin",
"vendor.alpha": "alpha",
"vendor.beta": "beta",
"vendor.special": "special",
"vendor.special.child": "special.child",
}
for visible, want := range wantResolutions {
if got := resolutions["."][visible]; got != want {
t.Errorf("resolution for %q = %q, want %q", visible, got, want)
}
}
}
func TestDiscoverSourcePackagesRejectsAmbiguousMergedSources(t *testing.T) {
root := t.TempDir()
app := filepath.Join(root, "app")
first := filepath.Join(root, "first")
second := filepath.Join(root, "second")
writeTestSource(t, filepath.Join(app, "main.qk"), "module main\nimport vendor.foo\nlet main() {}\n")
writeTestSource(t, filepath.Join(first, "foo", "main.qk"), "module foo\n")
writeTestSource(t, filepath.Join(second, "foo", "main.qk"), "module foo\n")
mounts := []sourceMount{
{Kind: sourceMountCollection, Prefix: "vendor", Directory: first},
{Kind: sourceMountCollection, Prefix: "vendor", Directory: second},
}
_, _, _, _, _, err := discoverSourcePackages(".", app, "", []string{app}, mounts, "", false)
if err == nil || !strings.Contains(err.Error(), "is ambiguous") {
t.Fatalf("expected ambiguous source error, got %v", err)
}
}
func TestDiscoverSourcePackagesRejectsExactCollectionConflict(t *testing.T) {
root := t.TempDir()
app := filepath.Join(root, "app")
collection := filepath.Join(root, "collection")
exact := filepath.Join(root, "exact")
writeTestSource(t, filepath.Join(app, "main.qk"), "module main\nimport vendor.foo\nlet main() {}\n")
writeTestSource(t, filepath.Join(collection, "foo", "main.qk"), "module foo\n")
writeTestSource(t, filepath.Join(exact, "main.qk"), "module foo\n")
mounts := []sourceMount{
{Kind: sourceMountCollection, Prefix: "vendor", Directory: collection},
{Kind: sourceMountExact, Prefix: "vendor.foo", Directory: exact, SemanticBase: "foo"},
}
_, _, _, _, _, err := discoverSourcePackages(".", app, "", []string{app}, mounts, "", false)
if err == nil || !strings.Contains(err.Error(), "is ambiguous") {
t.Fatalf("expected exact/collection conflict, got %v", err)
}
}