Files
orca/internal/security/flock_test.go
T
Jon Chery 60b0357eb6 feat(P0c): Job/Service/DaemonSet schemas + emitter interface + systemd stub (REQ-074)
P0c — Kind-specific schema validators + Layer 4 emitter interface.

Schemas (internal/spec/schema/schema.go, REQ-074):
- Validator interface with JobValidator, ServiceValidator, DaemonSetValidator.
  JobValidator: count=1, no service block, optional schedule/timeout.
  ServiceValidator: ports required, count>=1, restart+update+runtime required.
  DaemonSetValidator: schedule mode required, no ports (D-175), no count.
  ValidatorFor(kind) dispatcher. 96.2% coverage.

Emitter interface (internal/emitter/emitter.go, REQ-074, I-B-002):
- File{Path,Content,Mode}, Emitter interface { Render(spec,node) []File },
  Registry keyed by kind:runtime, Register + Render lookup. 100% coverage.

Systemd stub (internal/emitter/systemd.go):
- SystemdEmitter for process runtime. Renders minimal [Service] unit at
  /etc/systemd/system/orca-v1-alloc-<name>.service (orca-v1- prefix per
  dual-write window REQ-090 — no overlap with v0.8 daemon's orca-<job>).

Flock test fix: TestFlock_concurrentBlocks rewritten to use non-blocking
tryFlockEx (LOCK_NB) instead of a leaked blocking goroutine. Eliminates
the temp-dir cleanup race.

20 packages pass, 20 bats pass, gofmt clean, verify-reqs 90 consistent.

---ci---
project: orca
phase: P0c
milestone: v0.9
status: execute
---/ci---
2026-08-05 17:17:02 +00:00

83 lines
1.8 KiB
Go

package security
import (
"os"
"path/filepath"
"testing"
"time"
)
func TestFlock_acquireAndRelease(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "test.lock")
release, err := Flock(path)
if err != nil {
t.Fatalf("Flock: %v", err)
}
if _, statErr := os.Stat(path); statErr != nil {
t.Fatalf("lock file not created: %v", statErr)
}
release()
}
func TestFlock_reentrantAfterRelease(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "test.lock")
r1, err := Flock(path)
if err != nil {
t.Fatalf("first Flock: %v", err)
}
r1()
r2, err := Flock(path)
if err != nil {
t.Fatalf("second Flock after release: %v", err)
}
r2()
}
func TestFlock_concurrentBlocks(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "test.lock")
r1, err := Flock(path)
if err != nil {
t.Fatalf("first Flock: %v", err)
}
// Give the blocking goroutine a chance to start and block.
time.Sleep(50 * time.Millisecond)
// Verify the second lock is blocked by checking it hasn't acquired after a short window.
// Use a non-blocking attempt: open the file and try LOCK_EX|LOCK_NB.
blocked := make(chan bool, 1)
go func() {
f, err := os.OpenFile(path, os.O_RDWR, 0o600)
if err != nil {
blocked <- false
return
}
defer f.Close()
// LOCK_NB = non-blocking; returns EWOULDBLOCK if locked.
if err := tryFlockEx(int(f.Fd())); err != nil {
blocked <- true // got EWOULDBLOCK = the lock is held by r1
return
}
releaseFlock(int(f.Fd()))
blocked <- false // acquired = r1 didn't hold the lock (bug)
}()
select {
case b := <-blocked:
if !b {
t.Fatal("second lock acquired while first holds it — lock not working")
}
case <-time.After(2 * time.Second):
t.Fatal("non-blocking try-lock timed out")
}
r1()
}