package engine import ( "bytes" "errors" "fmt" "strings" "sync" "testing" ) type independentText string func (value independentText) MarshalText() ([]byte, error) { return []byte(value), nil } func (value *independentText) UnmarshalText(text []byte) error { *value = independentText(text) return nil } type independentConfig struct { Labels map[string]string `yaml:"labels"` Items []string `yaml:"items"` Custom independentText `yaml:"custom"` } func TestParallelLoadsShareRulesButNotMutableValues(t *testing.T) { definition := Definition[independentConfig]{ Defaults: func() independentConfig { return independentConfig{ Labels: map[string]string{"default": "kept"}, Items: []string{"default"}, Custom: "default", } }, } const count = 32 results := make([]*testResult[independentConfig], count) errorsByIndex := make([]error, count) var group sync.WaitGroup for index := range count { group.Add(1) go func(index int) { defer group.Done() input := fmt.Sprintf("custom: value-%d\n", index) results[index], errorsByIndex[index] = testLoad(Bytes("parallel.yml", []byte(input)), Options[independentConfig]{Definition: definition}) }(index) } group.Wait() for index, err := range errorsByIndex { if err != nil { t.Fatalf("testLoad(%d) failed: %v", index, err) } } results[0].Config.Labels["default"] = "mutated" results[0].Config.Items[0] = "mutated" results[0].Config.Custom = "mutated" for index := 1; index < count; index++ { if results[index].Config.Labels["default"] != "kept" || results[index].Config.Items[0] != "default" || results[index].Config.Custom != independentText(fmt.Sprintf("value-%d", index)) { t.Fatalf("result %d shares mutable state: %#v", index, results[index].Config) } } var effective bytes.Buffer if err := results[0].WriteEffective(&effective); err != nil { t.Fatal(err) } if strings.Contains(effective.String(), "mutated") || !strings.Contains(effective.String(), "default: kept") || !strings.Contains(effective.String(), "value-0") { t.Fatalf("frozen effective snapshot = %q", effective.String()) } } func TestLoadedConfigurationIsSeparateFromInspection(t *testing.T) { type configuration struct { Value string `yaml:"value"` } loaded, err := Load[configuration](Bytes("config.yml", []byte("value: original\n")), testSecretBinding()) if err != nil { t.Fatal(err) } if loaded.Inspection == nil { t.Fatal("inspection is nil") } loaded.Config.Value = "mutated" var effective bytes.Buffer if err := loaded.Inspection.WriteEffective(&effective); err != nil { t.Fatal(err) } if effective.String() != "value: original\n" { t.Fatalf("inspection changed with configuration: %q", effective.String()) } } func TestConcurrentLoadsOfCopiedReaderSourceHaveOneClaim(t *testing.T) { source := Reader("shared", strings.NewReader("value: one\n")) copied := source type config struct { Value string `yaml:"value"` } start := make(chan struct{}) errorsFound := make(chan error, 2) var group sync.WaitGroup for _, candidate := range []Source{source, copied} { group.Add(1) go func(candidate Source) { defer group.Done() <-start _, err := testLoad[config](candidate) errorsFound <- err }(candidate) } close(start) group.Wait() close(errorsFound) successes, used := 0, 0 for err := range errorsFound { if err == nil { successes++ continue } var loadError *LoadError if errors.As(err, &loadError) { diagnostics := loadError.Diagnostics() if len(diagnostics) == 1 && diagnostics[0].Code == CodeSourceReaderUsed { used++ } } } if successes != 1 || used != 1 { t.Fatalf("successes=%d reader-used=%d", successes, used) } }