test-session-start-codex.sh 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
  4. REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
  5. HOOK_UNDER_TEST="$REPO_ROOT/hooks/session-start-codex"
  6. CONFIG_UNDER_TEST="$REPO_ROOT/hooks/hooks-codex.json"
  7. FAILURES=0
  8. pass() {
  9. echo " [PASS] $1"
  10. }
  11. fail() {
  12. echo " [FAIL] $1"
  13. FAILURES=$((FAILURES + 1))
  14. }
  15. # run_hook <stdin-payload> — echoes hook stdout; fails the calling test on
  16. # non-zero exit. env -i mirrors the codex hook executor's clean environment.
  17. run_hook() {
  18. printf '%s' "$1" | env -i PATH="${PATH:-}" bash "$HOOK_UNDER_TEST"
  19. }
  20. echo "Codex SessionStart hook tests"
  21. startup_payload='{"session_id":"s","hook_event_name":"SessionStart","model":"gpt-5.6-terra","source":"startup"}'
  22. if output="$(run_hook "$startup_payload")" && [ -z "$output" ]; then
  23. pass "source=startup emits nothing and exits 0"
  24. else
  25. fail "source=startup emits nothing and exits 0"
  26. printf '%s\n' "$output" | head -3 | sed 's/^/ /'
  27. fi
  28. compact_payload='{"session_id":"s","hook_event_name":"SessionStart","model":"gpt-5.6-terra","source":"compact"}'
  29. if output="$(run_hook "$compact_payload")"; then
  30. ok=1
  31. for needle in \
  32. "<EXTREMELY_IMPORTANT>" \
  33. "You have superpowers." \
  34. "name: using-superpowers" \
  35. "<CONTEXT_RESTORED>" \
  36. "subagent-driven-development/SKILL.md" \
  37. "references/codex-tools.md"; do
  38. if [[ "$output" != *"$needle"* ]]; then
  39. ok=0
  40. echo " missing: $needle"
  41. fi
  42. done
  43. if [ "$ok" -eq 1 ]; then
  44. pass "source=compact emits bootstrap plus re-read addendum"
  45. else
  46. fail "source=compact emits bootstrap plus re-read addendum"
  47. fi
  48. else
  49. fail "source=compact emits bootstrap plus re-read addendum (hook exited non-zero)"
  50. fi
  51. # Whitespace-tolerant source matching (serializers vary).
  52. spaced_payload='{"hook_event_name":"SessionStart", "source" : "compact"}'
  53. if output="$(run_hook "$spaced_payload")" && [[ "$output" == *"<CONTEXT_RESTORED>"* ]]; then
  54. pass "whitespace around the source key still triggers injection"
  55. else
  56. fail "whitespace around the source key still triggers injection"
  57. fi
  58. if output="$(printf '' | env -i PATH="${PATH:-}" bash "$HOOK_UNDER_TEST")" && [ -z "$output" ]; then
  59. pass "empty stdin fails open to no output, exit 0"
  60. else
  61. fail "empty stdin fails open to no output, exit 0"
  62. fi
  63. if output="$(run_hook 'not json at all {{{')" && [ -z "$output" ]; then
  64. pass "garbage stdin fails open to no output, exit 0"
  65. else
  66. fail "garbage stdin fails open to no output, exit 0"
  67. fi
  68. # A compact mention inside some other field must not trigger injection.
  69. decoy_payload='{"hook_event_name":"SessionStart","source":"startup","cwd":"/tmp/compact"}'
  70. if output="$(run_hook "$decoy_payload")" && [ -z "$output" ]; then
  71. pass "compact appearing outside the source field does not trigger"
  72. else
  73. fail "compact appearing outside the source field does not trigger"
  74. fi
  75. if node -e '
  76. const config = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"));
  77. const group = config.hooks.SessionStart[0];
  78. if (group.matcher !== "compact") {
  79. console.error(`hook matcher is ${JSON.stringify(group.matcher)}, expected "compact"`);
  80. process.exit(1);
  81. }
  82. const entry = group.hooks[0];
  83. if (entry.type !== "command") {
  84. console.error(`hook type is ${JSON.stringify(entry.type)}, expected "command"`);
  85. process.exit(1);
  86. }
  87. if (!entry.command.includes("${PLUGIN_ROOT}") || !/run-hook\.cmd" session-start-codex$/.test(entry.command)) {
  88. console.error(`unexpected command shape: ${entry.command}`);
  89. process.exit(1);
  90. }
  91. ' "$CONFIG_UNDER_TEST"; then
  92. pass "hooks-codex.json runs session-start-codex via \${PLUGIN_ROOT} on compact"
  93. else
  94. fail "hooks-codex.json runs session-start-codex via \${PLUGIN_ROOT} on compact"
  95. fi
  96. if [[ "$FAILURES" -gt 0 ]]; then
  97. echo "STATUS: FAILED ($FAILURES failure(s))"
  98. exit 1
  99. fi
  100. echo "STATUS: PASSED"