Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4250e9f9f9 |
@@ -1,12 +1,9 @@
|
||||
// @vitest-environment jsdom
|
||||
/**
|
||||
* Tests for theme-cookie.ts:
|
||||
* - THEME_COOKIE constant
|
||||
* - readThemeCookie
|
||||
* - themeBootScript
|
||||
* Tests for readThemeCookie — parses a cookie value into a ThemePreference.
|
||||
*/
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { readThemeCookie, THEME_COOKIE, themeBootScript } from "../theme-cookie";
|
||||
import { readThemeCookie } from "../theme-cookie";
|
||||
|
||||
describe("readThemeCookie", () => {
|
||||
it('returns "light" when cookie value is "light"', () => {
|
||||
@@ -48,63 +45,3 @@ describe("readThemeCookie", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ── THEME_COOKIE ────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("THEME_COOKIE", () => {
|
||||
it("is a non-empty string", () => {
|
||||
expect(typeof THEME_COOKIE).toBe("string");
|
||||
expect(THEME_COOKIE.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("equals 'mol_theme'", () => {
|
||||
expect(THEME_COOKIE).toBe("mol_theme");
|
||||
});
|
||||
|
||||
it("is stable — constant is not reassigned", () => {
|
||||
const first = THEME_COOKIE;
|
||||
const second = THEME_COOKIE;
|
||||
expect(first).toBe(second);
|
||||
});
|
||||
});
|
||||
|
||||
// ── themeBootScript ─────────────────────────────────────────────────────────────
|
||||
|
||||
describe("themeBootScript", () => {
|
||||
it("is a non-empty string", () => {
|
||||
expect(typeof themeBootScript).toBe("string");
|
||||
expect(themeBootScript.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("contains THEME_COOKIE value in the cookie-regex pattern", () => {
|
||||
// The script reads document.cookie looking for mol_theme=...
|
||||
expect(themeBootScript).toContain(THEME_COOKIE);
|
||||
});
|
||||
|
||||
it("contains 'system', 'light', 'dark' in the match pattern", () => {
|
||||
expect(themeBootScript).toContain("system");
|
||||
expect(themeBootScript).toContain("light");
|
||||
expect(themeBootScript).toContain("dark");
|
||||
});
|
||||
|
||||
it("contains data-theme assignment on documentElement", () => {
|
||||
// The script sets document.documentElement.dataset.theme = resolved
|
||||
expect(themeBootScript).toContain("dataset.theme");
|
||||
expect(themeBootScript).toContain("document.documentElement");
|
||||
});
|
||||
|
||||
it("contains matchMedia call for OS preference fallback", () => {
|
||||
expect(themeBootScript).toContain("matchMedia");
|
||||
expect(themeBootScript).toContain("prefers-color-scheme");
|
||||
});
|
||||
|
||||
it("wraps the entire body in an IIFE so it runs immediately", () => {
|
||||
expect(themeBootScript).toMatch(/^\(\(\)=>/);
|
||||
});
|
||||
|
||||
it("is pure — constant evaluated once, same value every time", () => {
|
||||
const a = themeBootScript;
|
||||
const b = themeBootScript;
|
||||
expect(a).toBe(b);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
// @vitest-environment jsdom
|
||||
"use client";
|
||||
/**
|
||||
* Tests for theme-provider.tsx:
|
||||
* - applyResolvedTheme — pure DOM side-effect function
|
||||
* - ThemeProvider — context, setTheme, resolvedTheme derivation
|
||||
* - useTheme — hook + noop fallback
|
||||
*
|
||||
* Coverage gaps filled vs theme-cookie.test.ts (which tests only readThemeCookie):
|
||||
* applyResolvedTheme, ThemeProvider initialTheme, resolvedTheme derivation
|
||||
* from system preference, writeThemeCookie integration, useTheme noop fallback.
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import React from "react";
|
||||
import { render, screen, cleanup, act, waitFor } from "@testing-library/react";
|
||||
import { applyResolvedTheme, ThemeProvider, useTheme } from "../theme-provider";
|
||||
|
||||
// ─── applyResolvedTheme ────────────────────────────────────────────────────────
|
||||
|
||||
describe("applyResolvedTheme", () => {
|
||||
beforeEach(() => {
|
||||
if (typeof document !== "undefined") {
|
||||
delete (document.documentElement as Record<string, unknown>).dataset;
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
if (typeof document !== "undefined") {
|
||||
delete (document.documentElement as Record<string, unknown>).dataset;
|
||||
}
|
||||
});
|
||||
|
||||
it('sets data-theme="light" on document.documentElement', () => {
|
||||
applyResolvedTheme("light");
|
||||
expect(document.documentElement.dataset.theme).toBe("light");
|
||||
});
|
||||
|
||||
it('sets data-theme="dark" on document.documentElement', () => {
|
||||
applyResolvedTheme("dark");
|
||||
expect(document.documentElement.dataset.theme).toBe("dark");
|
||||
});
|
||||
|
||||
it("is idempotent — calling twice with same value keeps the same attribute", () => {
|
||||
applyResolvedTheme("dark");
|
||||
applyResolvedTheme("dark");
|
||||
expect(document.documentElement.dataset.theme).toBe("dark");
|
||||
});
|
||||
|
||||
it("is a pure function for its DOM side-effect — no return value", () => {
|
||||
expect(applyResolvedTheme("light")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("guards against undefined document (SSR safety)", () => {
|
||||
// In Node.js / SSR context document is undefined; the function returns
|
||||
// early without throwing. We simulate this by temporarily deleting document.
|
||||
const saved = globalThis.document;
|
||||
// @ts-expect-error — intentionally undefined for SSR test
|
||||
globalThis.document = undefined;
|
||||
expect(() => applyResolvedTheme("dark")).not.toThrow();
|
||||
globalThis.document = saved;
|
||||
});
|
||||
});
|
||||
|
||||
// ─── ThemeProvider ─────────────────────────────────────────────────────────────
|
||||
|
||||
describe("ThemeProvider", () => {
|
||||
beforeEach(() => {
|
||||
// Stub matchMedia so ThemeProvider's system-preference useEffect works in jsdom.
|
||||
// Default to light mode (matches=false) so resolvedTheme="light" when theme="system".
|
||||
Object.defineProperty(window, "matchMedia", {
|
||||
writable: true,
|
||||
value: vi.fn().mockImplementation((query: string) => ({
|
||||
matches: false, // light preference by default
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
})),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
if (typeof document !== "undefined") {
|
||||
delete (document.documentElement as Record<string, unknown>).dataset;
|
||||
}
|
||||
// Clear cookies set by writeThemeCookie.
|
||||
if (typeof document !== "undefined") {
|
||||
document.cookie = "mol_theme=; Max-Age=0";
|
||||
}
|
||||
});
|
||||
|
||||
function ThemeChild() {
|
||||
const { theme, resolvedTheme, setTheme } = useTheme();
|
||||
return (
|
||||
<div>
|
||||
<span data-testid="theme">{theme}</span>
|
||||
<span data-testid="resolved">{resolvedTheme}</span>
|
||||
<button
|
||||
data-testid="set-light"
|
||||
onClick={() => setTheme("light")}
|
||||
>
|
||||
light
|
||||
</button>
|
||||
<button
|
||||
data-testid="set-dark"
|
||||
onClick={() => setTheme("dark")}
|
||||
>
|
||||
dark
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
it("renders children", () => {
|
||||
render(
|
||||
<ThemeProvider initialTheme="light">
|
||||
<span data-testid="child">Hello</span>
|
||||
</ThemeProvider>,
|
||||
);
|
||||
expect(screen.getByTestId("child")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('initialTheme="light" sets theme=light', () => {
|
||||
render(
|
||||
<ThemeProvider initialTheme="light">
|
||||
<ThemeChild />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
expect(screen.getByTestId("theme").textContent).toBe("light");
|
||||
});
|
||||
|
||||
it('initialTheme="dark" sets theme=dark', () => {
|
||||
render(
|
||||
<ThemeProvider initialTheme="dark">
|
||||
<ThemeChild />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
expect(screen.getByTestId("theme").textContent).toBe("dark");
|
||||
});
|
||||
|
||||
it('initialTheme="system" falls back to light (matchMedia stub)', () => {
|
||||
// matchMedia is not stubbed in jsdom by default; the provider calls it
|
||||
// and reads the OS preference. Without a stub, jsdom returns
|
||||
// { matches: false } → "light".
|
||||
render(
|
||||
<ThemeProvider initialTheme="system">
|
||||
<ThemeChild />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
// Resolved is "light" because jsdom matchMedia stub returns false for dark.
|
||||
expect(screen.getByTestId("resolved").textContent).toBe("light");
|
||||
});
|
||||
|
||||
it("setTheme('dark') updates both theme and resolvedTheme", async () => {
|
||||
render(
|
||||
<ThemeProvider initialTheme="light">
|
||||
<ThemeChild />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
expect(screen.getByTestId("theme").textContent).toBe("light");
|
||||
|
||||
await act(async () => {
|
||||
screen.getByTestId("set-dark").click();
|
||||
});
|
||||
|
||||
expect(screen.getByTestId("theme").textContent).toBe("dark");
|
||||
// resolvedTheme tracks theme when not in system mode.
|
||||
expect(screen.getByTestId("resolved").textContent).toBe("dark");
|
||||
});
|
||||
|
||||
it("setTheme('light') updates both theme and resolvedTheme", async () => {
|
||||
render(
|
||||
<ThemeProvider initialTheme="dark">
|
||||
<ThemeChild />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
await act(async () => {
|
||||
screen.getByTestId("set-light").click();
|
||||
});
|
||||
expect(screen.getByTestId("theme").textContent).toBe("light");
|
||||
expect(screen.getByTestId("resolved").textContent).toBe("light");
|
||||
});
|
||||
|
||||
it("writes mol_theme cookie when setTheme is called", async () => {
|
||||
render(
|
||||
<ThemeProvider initialTheme="light">
|
||||
<ThemeChild />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
await act(async () => {
|
||||
screen.getByTestId("set-dark").click();
|
||||
});
|
||||
expect(document.cookie).toContain("mol_theme=dark");
|
||||
});
|
||||
|
||||
it("calls applyResolvedTheme on mount (data-theme set on <html>)", () => {
|
||||
render(
|
||||
<ThemeProvider initialTheme="dark">
|
||||
<span data-testid="child">hi</span>
|
||||
</ThemeProvider>,
|
||||
);
|
||||
expect(document.documentElement.dataset.theme).toBe("dark");
|
||||
});
|
||||
|
||||
it("calls applyResolvedTheme when resolvedTheme changes", async () => {
|
||||
render(
|
||||
<ThemeProvider initialTheme="light">
|
||||
<ThemeChild />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
// Start at light.
|
||||
expect(document.documentElement.dataset.theme).toBe("light");
|
||||
|
||||
await act(async () => {
|
||||
screen.getByTestId("set-dark").click();
|
||||
});
|
||||
|
||||
expect(document.documentElement.dataset.theme).toBe("dark");
|
||||
});
|
||||
});
|
||||
|
||||
// ─── useTheme noop fallback ────────────────────────────────────────────────────
|
||||
|
||||
describe("useTheme without ThemeProvider", () => {
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it("useTheme returns noopTheme when no provider is in the tree", () => {
|
||||
function ShowTheme() {
|
||||
const { theme, resolvedTheme, setTheme } = useTheme();
|
||||
return (
|
||||
<div>
|
||||
<span data-testid="theme">{theme}</span>
|
||||
<span data-testid="resolved">{resolvedTheme}</span>
|
||||
<span data-testid="setTheme-type">{typeof setTheme}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
render(<ShowTheme />);
|
||||
// noopTheme defaults: theme="system", resolvedTheme="light", setTheme no-op.
|
||||
expect(screen.getByTestId("theme").textContent).toBe("system");
|
||||
expect(screen.getByTestId("resolved").textContent).toBe("light");
|
||||
expect(screen.getByTestId("setTheme-type").textContent).toBe("function");
|
||||
});
|
||||
|
||||
it("setTheme is a no-op when no provider is present (no throw)", async () => {
|
||||
let threw = false;
|
||||
function ClickSetTheme() {
|
||||
const { setTheme } = useTheme();
|
||||
return (
|
||||
<button
|
||||
data-testid="call-setTheme"
|
||||
onClick={() => {
|
||||
try {
|
||||
setTheme("dark");
|
||||
} catch {
|
||||
threw = true;
|
||||
}
|
||||
}}
|
||||
>
|
||||
call
|
||||
</button>
|
||||
);
|
||||
}
|
||||
render(<ClickSetTheme />);
|
||||
await act(async () => {
|
||||
screen.getByTestId("call-setTheme").click();
|
||||
});
|
||||
expect(threw).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -75,7 +75,7 @@ function writeThemeCookie(value: ThemePreference): void {
|
||||
document.cookie = parts.join("; ");
|
||||
}
|
||||
|
||||
export function applyResolvedTheme(resolved: ResolvedTheme): void {
|
||||
function applyResolvedTheme(resolved: ResolvedTheme): void {
|
||||
if (typeof document === "undefined") return;
|
||||
document.documentElement.dataset.theme = resolved;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -444,3 +447,178 @@ func TestAdminSchedulesHealth_ResponseFields(t *testing.T) {
|
||||
t.Fatalf("unmet expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ── classifyScheduleStatus — additional edge cases ─────────────────────────────────
|
||||
|
||||
func TestClassifyScheduleStatus_ZeroThreshold(t *testing.T) {
|
||||
now := time.Now()
|
||||
lastRun := now.Add(-365 * 24 * time.Hour) // very old
|
||||
result := classifyScheduleStatus(&lastRun, 0, now)
|
||||
if result != "ok" {
|
||||
t.Errorf("classifyScheduleStatus(threshold=0) = %q; want 'ok'", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassifyScheduleStatus_NegativeThreshold(t *testing.T) {
|
||||
now := time.Now()
|
||||
lastRun := now.Add(-24 * time.Hour)
|
||||
result := classifyScheduleStatus(&lastRun, -1*time.Hour, now)
|
||||
if result != "ok" {
|
||||
t.Errorf("classifyScheduleStatus(threshold=-1h) = %q; want 'ok'", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassifyScheduleStatus_ExactlyAtThreshold(t *testing.T) {
|
||||
// Strict >: if now.Sub(lastRun) == threshold, it is NOT stale
|
||||
now := time.Date(2026, 5, 18, 12, 0, 0, 0, time.UTC)
|
||||
lastRun := time.Date(2026, 5, 18, 10, 0, 0, 0, time.UTC) // exactly 2h ago
|
||||
result := classifyScheduleStatus(&lastRun, 2*time.Hour, now)
|
||||
if result != "ok" {
|
||||
t.Errorf("classifyScheduleStatus(exactly at threshold) = %q; want 'ok'", result)
|
||||
}
|
||||
}
|
||||
|
||||
// ── loadRuntimeProvisionTimeouts (runtime_provision_timeouts.go) ─────────────────
|
||||
|
||||
func writeRuntimeConfigYAML(t *testing.T, tmpDir, templateName, runtime string, timeoutSecs int) {
|
||||
t.Helper()
|
||||
dir := filepath.Join(tmpDir, templateName)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
t.Fatalf("MkdirAll(%s): %v", dir, err)
|
||||
}
|
||||
yamlContent := "runtime: " + runtime + "\nruntime_config:\n provision_timeout_seconds: " + strconv.Itoa(timeoutSecs) + "\n"
|
||||
if err := os.WriteFile(filepath.Join(dir, "config.yaml"), []byte(yamlContent), 0644); err != nil {
|
||||
t.Fatalf("WriteFile: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRuntimeProvisionTimeouts_EmptyDir(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
result := loadRuntimeProvisionTimeouts(tmpDir)
|
||||
if len(result) != 0 {
|
||||
t.Errorf("loadRuntimeProvisionTimeouts(empty dir) len = %d; want 0", len(result))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRuntimeProvisionTimeouts_IgnoresNonDirEntries(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(tmpDir, "not-a-dir.yaml"), []byte("runtime: hermes\n"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
result := loadRuntimeProvisionTimeouts(tmpDir)
|
||||
if len(result) != 0 {
|
||||
t.Errorf("loadRuntimeProvisionTimeouts(file-only dir) len = %d; want 0", len(result))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRuntimeProvisionTimeouts_SingleTemplate(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
writeRuntimeConfigYAML(t, tmpDir, "tmpl-hermes", "hermes", 300)
|
||||
result := loadRuntimeProvisionTimeouts(tmpDir)
|
||||
if v, ok := result["hermes"]; !ok || v != 300 {
|
||||
t.Errorf("loadRuntimeProvisionTimeouts → hermes = %d; want 300", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRuntimeProvisionTimeouts_MultipleTemplatesSameRuntime(t *testing.T) {
|
||||
// Two templates using the same runtime — takes the MAX timeout
|
||||
tmpDir := t.TempDir()
|
||||
writeRuntimeConfigYAML(t, tmpDir, "tmpl-hermes-slow", "hermes", 600)
|
||||
writeRuntimeConfigYAML(t, tmpDir, "tmpl-hermes-fast", "hermes", 120)
|
||||
result := loadRuntimeProvisionTimeouts(tmpDir)
|
||||
if v, ok := result["hermes"]; !ok || v != 600 {
|
||||
t.Errorf("loadRuntimeProvisionTimeouts → hermes = %d; want 600 (max of 600, 120)", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRuntimeProvisionTimeouts_MultipleRuntimes(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
writeRuntimeConfigYAML(t, tmpDir, "tmpl-hermes", "hermes", 300)
|
||||
writeRuntimeConfigYAML(t, tmpDir, "tmpl-claude-code", "claude-code", 420)
|
||||
writeRuntimeConfigYAML(t, tmpDir, "tmpl-deepagents", "deepagents", 180)
|
||||
result := loadRuntimeProvisionTimeouts(tmpDir)
|
||||
want := map[string]int{
|
||||
"hermes": 300,
|
||||
"claude-code": 420,
|
||||
"deepagents": 180,
|
||||
}
|
||||
for runtime, wantSecs := range want {
|
||||
if got, ok := result[runtime]; !ok || got != wantSecs {
|
||||
t.Errorf("loadRuntimeProvisionTimeouts → %s = %d; want %d", runtime, got, wantSecs)
|
||||
}
|
||||
}
|
||||
if len(result) != len(want) {
|
||||
t.Errorf("loadRuntimeProvisionTimeouts → len = %d; want %d", len(result), len(want))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRuntimeProvisionTimeouts_IgnoresZeroTimeout(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
writeRuntimeConfigYAML(t, tmpDir, "tmpl-zero", "zero-runtime", 0)
|
||||
result := loadRuntimeProvisionTimeouts(tmpDir)
|
||||
if _, ok := result["zero-runtime"]; ok {
|
||||
t.Errorf("loadRuntimeProvisionTimeouts → 'zero-runtime' present; want absent (timeout=0)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRuntimeProvisionTimeouts_IgnoresNegativeTimeout(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
writeRuntimeConfigYAML(t, tmpDir, "tmpl-negative", "neg-runtime", -60)
|
||||
result := loadRuntimeProvisionTimeouts(tmpDir)
|
||||
if _, ok := result["neg-runtime"]; ok {
|
||||
t.Errorf("loadRuntimeProvisionTimeouts → 'neg-runtime' present; want absent (timeout<0)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRuntimeProvisionTimeouts_IgnoresMissingRuntimeField(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dir := filepath.Join(tmpDir, "tmpl-no-runtime")
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
yamlContent := "template_name: no-runtime-template\nruntime_config:\n provision_timeout_seconds: 300\n"
|
||||
if err := os.WriteFile(filepath.Join(dir, "config.yaml"), []byte(yamlContent), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
result := loadRuntimeProvisionTimeouts(tmpDir)
|
||||
if len(result) != 0 {
|
||||
t.Errorf("loadRuntimeProvisionTimeouts → len = %d; want 0 (runtime field absent)", len(result))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRuntimeProvisionTimeouts_IgnoresMalformedYAML(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dir := filepath.Join(tmpDir, "tmpl-bad-yaml")
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
badYAML := "runtime: bad\n provision_timeout_seconds: not a number\n"
|
||||
if err := os.WriteFile(filepath.Join(dir, "config.yaml"), []byte(badYAML), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
result := loadRuntimeProvisionTimeouts(tmpDir)
|
||||
if len(result) != 0 {
|
||||
t.Errorf("loadRuntimeProvisionTimeouts → len = %d; want 0 (malformed YAML)", len(result))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRuntimeProvisionTimeouts_IgnoresMissingConfig(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
if err := os.MkdirAll(filepath.Join(tmpDir, "tmpl-no-config"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writeRuntimeConfigYAML(t, tmpDir, "tmpl-good", "good-runtime", 300)
|
||||
result := loadRuntimeProvisionTimeouts(tmpDir)
|
||||
if v, ok := result["good-runtime"]; !ok || v != 300 {
|
||||
t.Errorf("loadRuntimeProvisionTimeouts → good-runtime = %d; want 300", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRuntimeProvisionTimeouts_IgnoresEmptyRuntime(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
writeRuntimeConfigYAML(t, tmpDir, "tmpl-empty", "", 300)
|
||||
result := loadRuntimeProvisionTimeouts(tmpDir)
|
||||
if len(result) != 0 {
|
||||
t.Errorf("loadRuntimeProvisionTimeouts → len = %d; want 0 (empty runtime)", len(result))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user