package config import ( "path/filepath" "testing" "time" ) // A deployment declares no format by default, and then it builds no exports at all: the door is // unmounted and `export_formats` is empty. The default matters because the alternative — announcing // a format because the engine that shipped with this build happened to know it — is a promise a // client discovers is empty on a user's first download. func TestADeploymentBuildsNoExportsUntilItSaysWhichFormats(t *testing.T) { t.Setenv("TM_PLATFORM_STATE_DIR", "/var/lib/tmplatform") t.Setenv("TM_PLATFORM_ENGINE_BIN", "/opt/tm/v1/tmctl") c, err := Load() if err != nil { t.Fatal(err) } if len(c.Export.Formats) != 0 || c.ExportsEnabled() { t.Fatalf("a deployment that declared nothing builds %v", c.Export.Formats) } t.Setenv("TM_PLATFORM_EXPORT_FORMATS", " epub , txt ") c, err = Load() if err != nil { t.Fatal(err) } if len(c.Export.Formats) != 2 || c.Export.Formats[0] != "epub" || c.Export.Formats[1] != "txt" { t.Fatalf("formats %q: the declaration is a comma-separated list and its order is the one "+ "`GET /capabilities` announces", c.Export.Formats) } if !c.ExportsEnabled() { t.Error("a deployment with formats, an engine and a directory still builds nothing") } // The directory falls out of the state directory: artifacts are platform state of exactly that // kind, and an operator who already said where state lives should not have to say it twice. if want := filepath.Join("/var/lib/tmplatform", "exports"); c.Export.Dir != want { t.Errorf("exports dir %q, want %q", c.Export.Dir, want) } if c.Export.TTL != 24*time.Hour { t.Errorf("TTL %s, want a day", c.Export.TTL) } } // An instance with formats but NO ENGINE builds nothing: `build` is an engine call like every other, // and a read replica that announced `epub` would answer a download with a failure every time. func TestAReadReplicaAnnouncesNoFormatsEvenIfTheOperatorDeclaredSome(t *testing.T) { t.Setenv("TM_PLATFORM_EXPORT_FORMATS", "epub") c, err := Load() if err != nil { t.Fatal(err) } if c.ExportsEnabled() { t.Error("an instance with no engine binary claims it can build exports") } } // The three refusals that belong at BOOT, where an operator sees them, rather than at the first // download, where they are one user's mysterious failure. // // ⚠ The relative directory is the sharpest of the three: the path is handed to the ENGINE as // `--out`, and the engine runs with the BOOK's directory as its working directory — so a relative // value would write every artifact inside somebody's book project, the one tree this platform must // not write into (D39.110). func TestTheExportSettingsFailAtLoadAndNotAtTheFirstDownload(t *testing.T) { for name, env := range map[string]map[string]string{ "a format that would name a path": {"TM_PLATFORM_EXPORT_FORMATS": "../etc"}, "a format declared twice": {"TM_PLATFORM_EXPORT_FORMATS": "epub,epub"}, "a relative artifact directory": {"TM_PLATFORM_EXPORTS_DIR": "var/exports"}, "a link that expires at once": {"TM_PLATFORM_EXPORT_TTL": "0s"}, "a malformed lifetime": {"TM_PLATFORM_EXPORT_TTL": "a while"}, } { t.Run(name, func(t *testing.T) { for k, v := range env { t.Setenv(k, v) } if _, err := Load(); err == nil { t.Errorf("%v was accepted", env) } }) } } // Every setting of this service is printed at boot with where it came from (PD-114), and a new one // that is not is a variable an operator hunts for and cannot find. func TestTheExportSettingsArePrintedWithTheirSource(t *testing.T) { t.Setenv("TM_PLATFORM_EXPORT_FORMATS", "epub") t.Setenv("TM_PLATFORM_EXPORTS_DIR", "/srv/exports") c, err := Load() if err != nil { t.Fatal(err) } printed := map[string]string{} for _, s := range c.Settings { printed[s.Key] = s.Source } for key, want := range map[string]string{ "TM_PLATFORM_EXPORT_FORMATS": "environment", "TM_PLATFORM_EXPORTS_DIR": "environment", "TM_PLATFORM_EXPORT_TTL": "default", } { if got, ok := printed[key]; !ok { t.Errorf("%s is not printed at boot", key) } else if got != want { t.Errorf("%s came from %q, want %q", key, got, want) } } }