Add maintained consumer migration examples
This commit is contained in:
252
examples/consumers/consumers_test.go
Normal file
252
examples/consumers/consumers_test.go
Normal file
@@ -0,0 +1,252 @@
|
||||
package consumers_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
"gitea.maximumdirect.net/eric/ConfigKit/examples/consumers"
|
||||
)
|
||||
|
||||
func Example_narratio() {
|
||||
result, err := consumers.LoadNarratio(fixture("narratio", "pipeline.yml"))
|
||||
artifact := result.Config.Artifacts["transcript"]
|
||||
fmt.Println(result.Config.ModelProfile, artifact.Output, result.Config.Timeout, artifact.Timeout, err)
|
||||
// Output: concise transcript.txt 6s 6s <nil>
|
||||
}
|
||||
|
||||
func Example_notarius() {
|
||||
result, err := consumers.LoadNotarius(fixture("notarius", "config.yml"))
|
||||
window, ok := consumers.NotariusWindow(result.Config, 0)
|
||||
fmt.Println(result.Config.Version, result.Config.Bindings[0].Object, window, ok, err)
|
||||
// Output: v1 summary 12 true <nil>
|
||||
}
|
||||
|
||||
func Example_weatherReporter() {
|
||||
defaults, defaultsErr := consumers.LoadWeather(configkit.NoSource())
|
||||
result, err := consumers.LoadWeather(configkit.File(fixture("weather", "config.yml")))
|
||||
discussion := result.Config.Reports["daily"].Modules[1]
|
||||
fmt.Println(defaults.Config.Units, result.Config.Units, discussion.Options.Sections[0], defaultsErr, err)
|
||||
// Output: metric us short_term <nil> <nil>
|
||||
}
|
||||
|
||||
func Example_seriatim() {
|
||||
speakers, speakerErr := consumers.LoadSpeakerRules(fixture("seriatim", "speakers.yml"))
|
||||
autocorrect, autocorrectErr := consumers.LoadAutocorrectRules(fixture("seriatim", "autocorrect.yml"))
|
||||
name, found := consumers.SpeakerName(speakers.Config, "Guest: teh event occured")
|
||||
text := consumers.ApplyAutocorrect(autocorrect.Config, "teh event occured")
|
||||
fmt.Println(name, found, text, speakerErr, autocorrectErr)
|
||||
// Output: visitor true the event occurred <nil> <nil>
|
||||
}
|
||||
|
||||
func Example_weatherStartup() {
|
||||
const blockedName = "CONFIGKIT_WEATHER_EXAMPLE_BLOCKED"
|
||||
const tokenName = "CONFIGKIT_WEATHER_EXAMPLE_TOKEN"
|
||||
restoreBlocked := preserveEnvironment(blockedName)
|
||||
restoreToken := preserveEnvironment(tokenName)
|
||||
defer restoreBlocked()
|
||||
defer restoreToken()
|
||||
_ = os.Setenv(blockedName, "")
|
||||
_ = os.Unsetenv(tokenName)
|
||||
|
||||
directory, _ := os.MkdirTemp("", "configkit-weather-example-")
|
||||
defer os.RemoveAll(directory)
|
||||
_ = os.WriteFile(filepath.Join(directory, blockedName), []byte("unused-example-value\n"), 0o600)
|
||||
_ = os.WriteFile(filepath.Join(directory, tokenName), []byte("disposable-example-credential\n"), 0o600)
|
||||
report, bootstrapErr := configkit.LoadSecretsDir(directory)
|
||||
var reportOutput bytes.Buffer
|
||||
_, reportWriteErr := report.WriteTo(&reportOutput)
|
||||
|
||||
result, loadErr := consumers.LoadWeather(configkit.Bytes("weather.yml", []byte("units: metric\ntoken: "+tokenName+"\n")),
|
||||
configkit.Override{Path: configkit.Keys("units"), Value: "us", Flag: "--units"})
|
||||
var explanation bytes.Buffer
|
||||
explainErr := result.WriteExplanation(&explanation, configkit.Keys("units"))
|
||||
blocked, loaded := secretStatus(report, blockedName), secretStatus(report, tokenName)
|
||||
value, present := os.LookupEnv(tokenName)
|
||||
fmt.Printf("blocked=%s loaded=%s report=%t reference=%s explained=%t unchanged=%t errors=%t\n",
|
||||
blocked, loaded, reportOutput.Len() > 0, result.Config.Token.EnvName(),
|
||||
strings.Contains(explanation.String(), "--units"), present && value == "disposable-example-credential",
|
||||
bootstrapErr != nil || reportWriteErr != nil || loadErr != nil || explainErr != nil)
|
||||
// Output: blocked=skipped_existing loaded=loaded report=true reference=CONFIGKIT_WEATHER_EXAMPLE_TOKEN explained=true unchanged=true errors=false
|
||||
}
|
||||
|
||||
func TestMaintainedConsumerFixturesPreserveDomainOutcomes(t *testing.T) {
|
||||
narratio, err := consumers.LoadNarratio(fixture("narratio", "pipeline.yml"))
|
||||
if err != nil || narratio.Config.Artifacts["transcript"].Output != "transcript.txt" ||
|
||||
narratio.Config.ModelProfile != "concise" || len(narratio.Sources()) != 2 {
|
||||
t.Fatalf("Narratio shape = %#v, %v", narratio, err)
|
||||
}
|
||||
if origin, ok := narratio.Metadata().Lookup(configkit.Keys("artifacts", "transcript", "output")); !ok || origin.Origin.Layer != configkit.LayerResolution {
|
||||
t.Fatalf("artifact origin = %#v, %v", origin, ok)
|
||||
}
|
||||
profiled, err := consumers.LoadNarratioProfile(fixture("narratio", "pipeline.yml"), "testing")
|
||||
if err != nil || profiled.Config.PollInterval.String() != "2m0s" ||
|
||||
profiled.Config.ModelProfile != "concise" || len(profiled.Sources()) != 3 {
|
||||
t.Fatalf("configuration/application profiles = %#v, %v", profiled, err)
|
||||
}
|
||||
|
||||
notarius, err := consumers.LoadNotarius(fixture("notarius", "config.yml"))
|
||||
window, converted := consumers.NotariusWindow(notarius.Config, 0)
|
||||
if err != nil || !converted || window != 12 {
|
||||
t.Fatalf("Notarius shape = %#v, %v, %v", notarius, converted, err)
|
||||
}
|
||||
if _, ok := notarius.Config.Bindings[0].Options["window_units"].(int64); !ok {
|
||||
t.Fatalf("dynamic integer type = %T", notarius.Config.Bindings[0].Options["window_units"])
|
||||
}
|
||||
|
||||
weather, err := consumers.LoadWeather(configkit.File(fixture("weather", "config.yml")))
|
||||
if err != nil || weather.Config.Reports["daily"].Modules[1].Options.Sections[0] != "short_term" {
|
||||
t.Fatalf("WeatherReporter shape = %#v, %v", weather, err)
|
||||
}
|
||||
|
||||
speakers, err := consumers.LoadSpeakerRules(fixture("seriatim", "speakers.yml"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
name, ok := consumers.SpeakerName(speakers.Config, "Host: introduction")
|
||||
if !ok || name != "narrator" || speakers.Config.Rules[0].Prefix != "Host:" {
|
||||
t.Fatalf("ordered speaker rules = %#v, %q, %v", speakers.Config.Rules, name, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrationFixturesFailWithStableCodesAndPaths(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
load func() error
|
||||
code configkit.Code
|
||||
path string
|
||||
}{
|
||||
{
|
||||
name: "integer schema version",
|
||||
load: func() error { _, err := consumers.LoadNotarius(fixture("notarius", "integer-version.yml")); return err },
|
||||
code: configkit.CodeVersionType, path: "/version",
|
||||
},
|
||||
{
|
||||
name: "module shorthand",
|
||||
load: func() error {
|
||||
_, err := consumers.LoadWeather(configkit.File(fixture("weather", "shorthand-module.yml")))
|
||||
return err
|
||||
},
|
||||
code: configkit.CodeDecodeType, path: "/reports/daily/modules/0",
|
||||
},
|
||||
{
|
||||
name: "explicit empty modules",
|
||||
load: func() error {
|
||||
_, err := consumers.LoadWeather(configkit.File(fixture("weather", "empty-modules.yml")))
|
||||
return err
|
||||
},
|
||||
code: configkit.CodeValidateError, path: "/reports/daily/modules",
|
||||
},
|
||||
{
|
||||
name: "explicit empty path templates",
|
||||
load: func() error {
|
||||
_, err := consumers.LoadWeather(configkit.File(fixture("weather", "empty-paths.yml")))
|
||||
return err
|
||||
},
|
||||
code: configkit.CodeValidateError, path: "/reports/daily/path_templates",
|
||||
},
|
||||
{
|
||||
name: "explicit null required option",
|
||||
load: func() error {
|
||||
_, err := consumers.LoadWeather(configkit.File(fixture("weather", "null-window.yml")))
|
||||
return err
|
||||
},
|
||||
code: configkit.CodeValidateError, path: "/reports/daily/modules/0/options/window_units",
|
||||
},
|
||||
{
|
||||
name: "strict unknown rule field",
|
||||
load: func() error {
|
||||
_, err := consumers.LoadSpeakerRules(fixture("seriatim", "unknown-field.yml"))
|
||||
return err
|
||||
},
|
||||
code: configkit.CodeDecodeUnknownField, path: "/rules/0/color",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var loadError *configkit.LoadError
|
||||
if err := test.load(); !errors.As(err, &loadError) {
|
||||
t.Fatalf("error = %T %v", err, err)
|
||||
}
|
||||
diagnostics := loadError.Diagnostics()
|
||||
if len(diagnostics) == 0 || diagnostics[0].Code != test.code || diagnostics[0].Path.String() != test.path {
|
||||
t.Fatalf("diagnostics = %#v", diagnostics)
|
||||
}
|
||||
if strings.Contains(diagnostics[0].Message, "wrong-before-profile") {
|
||||
t.Fatalf("diagnostic exposed raw parser input: %#v", diagnostics[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWeatherLoadedAndConstructedRulesAgreeWithoutMutation(t *testing.T) {
|
||||
loaded, err := consumers.LoadWeather(configkit.File(fixture("weather", "config.yml")))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
reconstructed, err := consumers.LoadConstructedWeather(loaded.Config)
|
||||
if err != nil || !reflect.DeepEqual(reconstructed.Config, loaded.Config) {
|
||||
t.Fatalf("loaded/constructed outcomes differ: %#v, %v", reconstructed, err)
|
||||
}
|
||||
constructed := consumers.WeatherConfig{Units: loaded.Config.Units, Reports: map[string]consumers.ReportConfig{}}
|
||||
constructed.Reports["daily"] = consumers.ReportConfig{
|
||||
Modules: []consumers.ModuleSpec{{Module: "discussion", Options: consumers.ModuleOptions{Sections: []string{" short_term "}}}},
|
||||
PathTemplates: []string{"daily/{date}.md"},
|
||||
}
|
||||
wantInput := constructed.Reports["daily"].Modules[0].Options.Sections[0]
|
||||
result, err := consumers.LoadConstructedWeather(constructed)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := constructed.Reports["daily"].Modules[0].Options.Sections[0]; got != wantInput {
|
||||
t.Fatalf("constructed options mutated from %q to %q", wantInput, got)
|
||||
}
|
||||
if result.Config.Reports["daily"].Modules[0].Options.Sections[0] != "short_term" {
|
||||
t.Fatalf("normalized result = %#v", result.Config.Reports["daily"])
|
||||
}
|
||||
|
||||
empty := consumers.WeatherConfig{Units: "metric", Reports: map[string]consumers.ReportConfig{
|
||||
"daily": {Modules: []consumers.ModuleSpec{}},
|
||||
}}
|
||||
_, err = consumers.LoadConstructedWeather(empty)
|
||||
var loadError *configkit.LoadError
|
||||
if !errors.As(err, &loadError) || loadError.Diagnostics()[0].Code != configkit.CodeValidateError {
|
||||
t.Fatalf("constructed empty modules error = %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(empty.Reports["daily"].Modules, []consumers.ModuleSpec{}) {
|
||||
t.Fatal("constructed empty module input was mutated")
|
||||
}
|
||||
}
|
||||
|
||||
func fixture(parts ...string) string {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
values := append([]string{filepath.Dir(file), "testdata"}, parts...)
|
||||
return filepath.Join(values...)
|
||||
}
|
||||
|
||||
func preserveEnvironment(name string) func() {
|
||||
value, present := os.LookupEnv(name)
|
||||
return func() {
|
||||
if present {
|
||||
_ = os.Setenv(name, value)
|
||||
} else {
|
||||
_ = os.Unsetenv(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func secretStatus(report *configkit.SecretsReport, name string) configkit.SecretFileStatus {
|
||||
for _, entry := range report.Entries() {
|
||||
if entry.Name == name {
|
||||
return entry.Status
|
||||
}
|
||||
}
|
||||
return "missing"
|
||||
}
|
||||
Reference in New Issue
Block a user