99 lines
4.0 KiB
Go
99 lines
4.0 KiB
Go
package qaopsorg
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestDirectoryBridgeRegressionSuiteRunsInDefaultCI(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
|
defer cancel()
|
|
cmd := exec.CommandContext(ctx, "python3", "-m", "unittest", "discover", "-s", "scripts", "-p", "test_qa_ops_directory_sync.py", "-v")
|
|
cmd.Dir = filepath.Join("..", "..")
|
|
if output, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("directory bridge regression failed: %v\n%s", err, output)
|
|
}
|
|
}
|
|
|
|
func TestDirectoryRunnerParsesPrivateOutputAndRemovesSpool(t *testing.T) {
|
|
t.Setenv("DINGTALK_CLIENT_SECRET", "fixture-credential-do-not-forward")
|
|
root := t.TempDir()
|
|
spool := filepath.Join(root, "spool")
|
|
if err := os.Mkdir(spool, 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
script := filepath.Join(root, "fake-sync.sh")
|
|
fixture := `{"source":"weekly_report_live","synced_at":"2026-10-01T00:00:00Z","complete":true,"directory":{"departments":[{"id":"root","parent_id":"","name":"隔离测试部门"}],"people":[{"id":"fixture-user","department_ids":["root"]}]}}`
|
|
body := "#!/bin/sh\nif [ -n \"${DINGTALK_CLIENT_SECRET:-}\" ]; then exit 3; fi\nwhile [ \"$#\" -gt 0 ]; do if [ \"$1\" = --output ]; then output=$2; shift; fi; shift; done\numask 077\nprintf '%s' '" + fixture + "' > \"$output\"\n"
|
|
if err := os.WriteFile(script, []byte(body), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
runner := ExecDirectoryRunner{Python: "/bin/sh", Script: script, RuntimeRoot: root, SpoolRoot: spool, Timeout: time.Second}
|
|
result, err := runner.Run(context.Background())
|
|
if err != nil || len(result.Directory.People) != 1 {
|
|
t.Fatalf("runner failed: %v", err)
|
|
}
|
|
entries, err := os.ReadDir(spool)
|
|
if err != nil || len(entries) != 0 {
|
|
t.Fatal("personnel spool leaked")
|
|
}
|
|
if err := os.WriteFile(script, []byte("#!/bin/sh\nprintf 'secret-driver-detail' >&2\nexit 1\n"), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = runner.Run(context.Background())
|
|
if err == nil || strings.Contains(err.Error(), "secret-driver-detail") {
|
|
t.Fatal("runner error is missing or leaks child stderr")
|
|
}
|
|
entries, _ = os.ReadDir(spool)
|
|
if len(entries) != 0 {
|
|
t.Fatal("failed runner leaked spool")
|
|
}
|
|
}
|
|
|
|
func TestDirectoryRunnerRejectsMalformedOrPublicPersonnelOutput(t *testing.T) {
|
|
root := t.TempDir()
|
|
for _, mode := range []string{"malformed", "public"} {
|
|
script := filepath.Join(root, mode+".sh")
|
|
body := "#!/bin/sh\numask 077\nwhile [ \"$#\" -gt 0 ]; do if [ \"$1\" = --output ]; then output=$2; shift; fi; shift; done\nprintf '%s' '{not-json}' > \"$output\"\n"
|
|
if mode == "public" {
|
|
body += "printf '%s' '{\"source\":\"weekly_report_live\",\"synced_at\":\"2026-10-01T00:00:00Z\",\"complete\":true,\"directory\":{\"departments\":[{\"id\":\"root\",\"parent_id\":\"\",\"name\":\"fixture\"}],\"people\":[{\"id\":\"fixture-user\",\"department_ids\":[\"root\"]}]}}' > \"$output\"\nchmod 644 \"$output\"\n"
|
|
}
|
|
if err := os.WriteFile(script, []byte(body), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
runner := ExecDirectoryRunner{Python: "/bin/sh", Script: script, RuntimeRoot: root, SpoolRoot: root, Timeout: time.Second}
|
|
if _, err := runner.Run(context.Background()); err == nil {
|
|
t.Fatal("invalid output accepted")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDirectoryRunnerDeadlineStopsChildAndCleansPrivateSpool(t *testing.T) {
|
|
root := t.TempDir()
|
|
spool := filepath.Join(root, "spool")
|
|
if err := os.Mkdir(spool, 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
script := filepath.Join(root, "timeout.sh")
|
|
if err := os.WriteFile(script, []byte("#!/bin/sh\nexec sleep 30\n"), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
runner := ExecDirectoryRunner{Python: "/bin/sh", Script: script, RuntimeRoot: root, SpoolRoot: spool, Timeout: 50 * time.Millisecond}
|
|
started := time.Now()
|
|
if _, err := runner.Run(context.Background()); err == nil {
|
|
t.Fatal("timed-out source reported success")
|
|
}
|
|
if time.Since(started) > 5*time.Second {
|
|
t.Fatal("source process cleanup is unbounded")
|
|
}
|
|
entries, err := os.ReadDir(spool)
|
|
if err != nil || len(entries) != 0 {
|
|
t.Fatal("timed-out source retained personnel spool")
|
|
}
|
|
}
|