internal/cli/env_test.go
Ref: Size: 4.4 KiB History
package cli
import (
"os"
"path/filepath"
"strings"
"testing"
)
// cleanEnv isolates a test from the developer's own laptop: every EITRI_*
// variable unset, and the config file pointed at an empty temp dir so FromEnv
// cannot read a real ~/.eitri/config.json. It returns the config path, for the
// tests that want to put a file there.
func cleanEnv(t *testing.T) string {
t.Helper()
dir := t.TempDir()
for _, v := range []string{
"EITRI_URL", "EITRI_GATE", "EITRI_TENANT", "EITRI_CA", "EITRI_KEY",
"EITRI_KNOWN_HOSTS", "EITRI_TOKEN",
} {
t.Setenv(v, "")
}
cfg := filepath.Join(dir, "config.json")
t.Setenv("EITRI_CONFIG", cfg)
t.Setenv("HOME", dir)
return cfg
}
func TestFromEnvDefaults(t *testing.T) {
cleanEnv(t)
t.Setenv("EITRI_URL", "http://192.0.2.10:8080")
t.Setenv("EITRI_GATE", "192.0.2.10:2222")
t.Setenv("EITRI_TENANT", "acme")
t.Setenv("HOME", "/home/u")
e, err := FromEnv()
if err != nil {
t.Fatal(err)
}
if e.URL != "http://192.0.2.10:8080" || e.Gate != "192.0.2.10:2222" || e.Tenant != "acme" {
t.Errorf("required fields: %+v", e)
}
for got, want := range map[string]string{
e.CA: filepath.Join("/home/u", ".ssh", "eitri_user_ca"),
e.Key: filepath.Join("/home/u", ".ssh", "id_ed25519"),
e.KnownHosts: filepath.Join("/home/u", ".ssh", "eitri_known_hosts"),
} {
if got != want {
t.Errorf("default: got %q want %q", got, want)
}
}
}
func TestFromEnvHostedDefaults(t *testing.T) {
// A clean environment targets the hosted service at eitri.sh — no
// configuration needed to reach it (TestFromEnvDefaults proves the env
// still overrides these). The gate is the exception: left empty here so
// `eitri ssh` can ask the plane to name it rather than assuming.
cleanEnv(t)
e, err := FromEnv()
if err != nil {
t.Fatalf("clean env must succeed: %v", err)
}
if e.URL != "https://console.eitri.sh" {
t.Errorf("hosted default URL = %q", e.URL)
}
if e.Gate != "" {
t.Errorf("gate = %q, want empty so the plane can name it", e.Gate)
}
}
func TestFromEnvTenantOptional(t *testing.T) {
// The tenant is optional: `eitri ssh` derives it from the credential when
// nothing pins it, so an unset tenant is not a FromEnv error.
cleanEnv(t)
t.Setenv("EITRI_URL", "http://192.0.2.10:8080")
e, err := FromEnv()
if err != nil {
t.Fatalf("unset tenant must be accepted: %v", err)
}
if e.Tenant != "" {
t.Errorf("tenant = %q, want empty", e.Tenant)
}
}
// TestFromEnvPrecedence is the whole layering in one test: an EITRI_* variable
// beats the config file, the config file beats the hosted default, and a field
// nobody set falls through to the default. This is what makes `eitri init`
// worth running — after it, a laptop with an empty environment still reaches
// its own plane as its own tenant.
func TestFromEnvPrecedence(t *testing.T) {
cfg := cleanEnv(t)
if err := SaveConfig(cfg, Config{
URL: "http://192.0.2.10:8080",
Gate: "192.0.2.10:2222",
Tenant: "acme",
CA: "/keys/ca",
Key: "/keys/user",
}); err != nil {
t.Fatal(err)
}
// File alone: every field comes from it.
e, err := FromEnv()
if err != nil {
t.Fatal(err)
}
if e.URL != "http://192.0.2.10:8080" || e.Gate != "192.0.2.10:2222" ||
e.Tenant != "acme" || e.CA != "/keys/ca" || e.Key != "/keys/user" {
t.Errorf("from config: %+v", e)
}
// The environment overrides it, field by field.
t.Setenv("EITRI_URL", "http://other:8080")
t.Setenv("EITRI_GATE", "other:2222")
t.Setenv("EITRI_TENANT", "beta")
e, err = FromEnv()
if err != nil {
t.Fatal(err)
}
if e.URL != "http://other:8080" || e.Gate != "other:2222" || e.Tenant != "beta" {
t.Errorf("env must override the file: %+v", e)
}
if e.CA != "/keys/ca" {
t.Errorf("an unset variable must leave the file's value: ca = %q", e.CA)
}
}
// A config that exists but does not parse — the half-written file an aborted
// run could leave — is an error naming the path, never a silent fallback to
// the hosted defaults while the user believes they are on their own plane.
func TestFromEnvRefusesUnreadableConfig(t *testing.T) {
cfg := cleanEnv(t)
if err := os.WriteFile(cfg, []byte(`{"url": "http://192.0`), 0o600); err != nil {
t.Fatal(err)
}
_, err := FromEnv()
if err == nil {
t.Fatal("a corrupt config must not resolve to the hosted defaults")
}
if got := err.Error(); !strings.Contains(got, cfg) || !strings.Contains(got, "eitri init") {
t.Errorf("error must name the file and the remedy: %v", err)
}
}