Files
orca/internal/cli/init_test.go
T
Jon Chery 04d9dccd41 fix(P01): register orca cert command tree + cert_repo tests (REQ-053)
The `orca cert` command (ca-init, gen, show, renew, fingerprint) was
fully implemented in internal/cli/cert.go but never registered on
rootCmd — unreachable from the CLI. Added init() registration (AD-022).
Added cert_test.go (regression) + cert_smoke_test.go (e2e). Added
cert_repo_test.go (11 tests) + migration 0007 (UNIQUE serial_hex, I-107).

---ci---
project: orca
phase: 1
milestone: v0.7
status: verify
requirements:
  covered: [REQ-053]
  partial: []
---/ci---
2026-08-04 00:05:10 +00:00

206 lines
5.6 KiB
Go

package cli
import (
"context"
"io"
"os"
"path/filepath"
"testing"
"time"
"git.cloudinit.dev/coreci/orca/internal/certpaths"
"git.cloudinit.dev/coreci/orca/internal/model"
"git.cloudinit.dev/coreci/orca/internal/store"
)
// initTestEnv sets ORCA_HOME to a temp dir and returns a cleanup func.
func initTestEnv(t *testing.T) (string, func()) {
t.Helper()
dir := t.TempDir()
orig := os.Getenv("ORCA_HOME")
if err := os.Setenv("ORCA_HOME", dir); err != nil {
t.Fatalf("set ORCA_HOME: %v", err)
}
return dir, func() {
if err := os.Setenv("ORCA_HOME", orig); err != nil {
t.Fatalf("restore ORCA_HOME: %v", err)
}
}
}
// discardWriter is an io.Writer that discards all output (for tests
// that don't need to inspect init stdout).
type discardWriter struct{}
func (discardWriter) Write(p []byte) (int, error) { return len(p), nil }
var _ io.Writer = discardWriter{}
func TestInit_FullBootstrap(t *testing.T) {
dir, cleanup := initTestEnv(t)
defer cleanup()
if err := runInit(discardWriter{}); err != nil {
t.Fatalf("init: %v", err)
}
// Verify namespace dir exists.
if _, err := os.Stat(dir); err != nil {
t.Errorf("namespace dir missing: %v", err)
}
// Verify CA files exist with correct modes.
caCert := certpaths.CACertPath()
caKey := certpaths.CAKeyPath()
if _, err := os.Stat(caCert); err != nil {
t.Errorf("ca.crt missing: %v", err)
}
if info, err := os.Stat(caKey); err == nil {
if info.Mode().Perm() != 0o600 {
t.Errorf("ca.key mode = %04o, want 0600", info.Mode().Perm())
}
} else {
t.Errorf("ca.key missing: %v", err)
}
// Verify server cert exists.
if _, err := os.Stat(certpaths.ServerCertPath()); err != nil {
t.Errorf("server.crt missing: %v", err)
}
// Verify DB exists and has migrations applied.
db, err := store.Open(certpaths.DBPath())
if err != nil {
t.Fatalf("open db: %v", err)
}
defer db.Close()
ctx := context.Background()
version, err := store.MigrationVersion(ctx, db)
if err != nil {
t.Fatalf("migration version: %v", err)
}
if version != "0007_certs_serial_unique.sql" {
t.Errorf("migration version = %q, want 0007_certs_serial_unique.sql", version)
}
// Verify localhost node registered with kind=localhost.
repo := store.NewNodeRepo(db)
node, err := repo.GetByName(ctx, "localhost")
if err != nil {
t.Fatalf("get localhost node: %v", err)
}
if node.Kind != string(model.NodeKindLocalhost) {
t.Errorf("node kind = %q, want localhost", node.Kind)
}
if node.OS == "" {
t.Errorf("node os is empty, expected detected value")
}
if node.Address != "localhost:8443" {
t.Errorf("node address = %q, want localhost:8443", node.Address)
}
}
func TestInit_IdempotentReRun(t *testing.T) {
_, cleanup := initTestEnv(t)
defer cleanup()
// First init.
if err := runInit(discardWriter{}); err != nil {
t.Fatalf("first init: %v", err)
}
// Capture first-run state.
caCertBefore, _ := os.ReadFile(certpaths.CACertPath())
serverCertBefore, _ := os.ReadFile(certpaths.ServerCertPath())
db, err := store.Open(certpaths.DBPath())
if err != nil {
t.Fatalf("open db: %v", err)
}
repo := store.NewNodeRepo(db)
ctx := context.Background()
nodeBefore, err := repo.GetByName(ctx, "localhost")
if err != nil {
t.Fatalf("get node before: %v", err)
}
nodeIDBefore := nodeBefore.ID
joinedAtBefore := nodeBefore.JoinedAt
if err := db.Close(); err != nil {
t.Fatalf("close db: %v", err)
}
// Wait a moment so last_seen can differ.
time.Sleep(50 * time.Millisecond)
// Second init (should be idempotent).
if err := runInit(discardWriter{}); err != nil {
t.Fatalf("second init: %v", err)
}
// CA and server cert must NOT have been regenerated.
caCertAfter, _ := os.ReadFile(certpaths.CACertPath())
serverCertAfter, _ := os.ReadFile(certpaths.ServerCertPath())
if string(caCertBefore) != string(caCertAfter) {
t.Error("CA was regenerated on re-run (D-036 violation)")
}
if string(serverCertBefore) != string(serverCertAfter) {
t.Error("server cert was regenerated on re-run (D-036 violation)")
}
// Node ID and joined_at must be unchanged; last_seen should be refreshed.
db, err = store.Open(certpaths.DBPath())
if err != nil {
t.Fatalf("reopen db: %v", err)
}
defer db.Close()
repo = store.NewNodeRepo(db)
nodeAfter, err := repo.GetByName(ctx, "localhost")
if err != nil {
t.Fatalf("get node after: %v", err)
}
if nodeAfter.ID != nodeIDBefore {
t.Errorf("node id changed: was %s, now %s (D-036 violation)", nodeIDBefore, nodeAfter.ID)
}
if !nodeAfter.JoinedAt.Equal(joinedAtBefore) {
t.Errorf("joined_at changed: was %v, now %v (D-036 violation)", joinedAtBefore, nodeAfter.JoinedAt)
}
if !nodeAfter.LastSeen.After(joinedAtBefore) {
t.Errorf("last_seen not refreshed: was %v, now %v", joinedAtBefore, nodeAfter.LastSeen)
}
// No duplicate localhost nodes.
nodes, err := repo.List(ctx)
if err != nil {
t.Fatalf("list nodes: %v", err)
}
localhostCount := 0
for _, n := range nodes {
if n.Name == "localhost" {
localhostCount++
}
}
if localhostCount != 1 {
t.Errorf("found %d localhost nodes, want 1 (idempotency)", localhostCount)
}
}
func TestInit_NamespaceDirCreation(t *testing.T) {
dir, cleanup := initTestEnv(t)
defer cleanup()
// The namespace dir is the ORCA_HOME temp dir itself — but let's
// point at a non-existent subdir to test MkdirAll.
subDir := filepath.Join(dir, "nested", "orca-state")
if err := os.Setenv("ORCA_HOME", subDir); err != nil {
t.Fatalf("set ORCA_HOME: %v", err)
}
if err := runInit(discardWriter{}); err != nil {
t.Fatalf("init with nested dir: %v", err)
}
if _, err := os.Stat(subDir); err != nil {
t.Errorf("nested namespace dir not created: %v", err)
}
}