@@ -803,9 +803,9 @@ test "Policy — SafetyLevel emoji" {
803803
804804test "Policy — actionLevel all L0 actions" {
805805 const l0_actions = [_ ]qt.ActionKind {
806- .farm_status , .arena_status , .doctor_scan , .train_status , .train_diagnose ,
807- .experiment_chart , .patent_status , .research_sacred , .ouroboros_status ,
808- .experience_recall , . introspection , .farm_evolve_status , .swarm_status ,
806+ .farm_status , .arena_status , .doctor_scan , .train_status , .train_diagnose ,
807+ .experiment_chart , .patent_status , .research_sacred , .ouroboros_status , .experience_recall ,
808+ .introspection , .farm_evolve_status , .swarm_status ,
809809 };
810810 for (l0_actions ) | action | {
811811 try std .testing .expectEqual (SafetyLevel .read_only , actionLevel (action ));
@@ -814,8 +814,9 @@ test "Policy — actionLevel all L0 actions" {
814814
815815test "Policy — actionLevel all L1 actions" {
816816 const l1_actions = [_ ]qt.ActionKind {
817- .doctor_quick , .doctor_heal , .ouroboros_cycle , .git_commit_state ,
818- .git_push , .issue_comment , .notify , .arena_battle , .experience_save , .fmt ,
817+ .doctor_quick , .doctor_heal , .ouroboros_cycle , .git_commit_state ,
818+ .git_push , .issue_comment , .notify , .arena_battle ,
819+ .experience_save , .fmt ,
819820 };
820821 for (l1_actions ) | action | {
821822 try std .testing .expectEqual (SafetyLevel .soft_write , actionLevel (action ));
@@ -824,8 +825,8 @@ test "Policy — actionLevel all L1 actions" {
824825
825826test "Policy — actionLevel all L2 actions" {
826827 const l2_actions = [_ ]qt.ActionKind {
827- .farm_recycle , .farm_evolve_step , .cloud_spawn , .cloud_kill ,
828- .cloud_cleanup , .issue_create , .swarm_decompose ,
828+ .farm_recycle , .farm_evolve_step , .cloud_spawn , .cloud_kill ,
829+ .cloud_cleanup , .issue_create , .swarm_decompose ,
829830 };
830831 for (l2_actions ) | action | {
831832 try std .testing .expectEqual (SafetyLevel .dangerous , actionLevel (action ));
@@ -1205,3 +1206,172 @@ test "Policy — Incident detailStr returns slice" {
12051206
12061207 try std .testing .expectEqualStrings (text , inc .detailStr ());
12071208}
1209+
1210+ // ═══════════════════════════════════════════════════════════════════════════════
1211+ // Additional SafetyLevel tests
1212+ // ═══════════════════════════════════════════════════════════════════════════════
1213+
1214+ test "Policy — SafetyLevel L0 safe" {
1215+ try std .testing .expectEqualStrings ("safe" , .L0 .label ());
1216+ try std .testing .expectEqualStrings ("🟢" , .L0 .emoji ());
1217+ }
1218+
1219+ test "Policy — SafetyLevel L1 caution" {
1220+ try std .testing .expectEqualStrings ("caution" , .L1 .label ());
1221+ try std .testing .expectEqualStrings ("🟡" , .L1 .emoji ());
1222+ }
1223+
1224+ test "Policy — SafetyLevel L2 danger" {
1225+ try std .testing .expectEqualStrings ("danger" , .L2 .label ());
1226+ try std .testing .expectEqualStrings ("🔴" , .L2 .emoji ());
1227+ }
1228+
1229+ test "Policy — SafetyLevel L3 critical" {
1230+ try std .testing .expectEqualStrings ("critical" , .L3 .label ());
1231+ try std .testing .expectEqualStrings ("⚠️" , .L3 .emoji ());
1232+ }
1233+
1234+ // ═══════════════════════════════════════════════════════════════════════════════
1235+ // IncidentKind all values
1236+ // ═══════════════════════════════════════════════════════════════════════════════
1237+
1238+ test "Policy — IncidentKind all values exist" {
1239+ _ = IncidentKind .alert ;
1240+ _ = IncidentKind .auto_action ;
1241+ _ = IncidentKind .auto_action_fail ;
1242+ _ = IncidentKind .human_command ;
1243+ _ = IncidentKind .approval ;
1244+ _ = IncidentKind .denial ;
1245+ _ = IncidentKind .escalation ;
1246+ }
1247+
1248+ // ═══════════════════════════════════════════════════════════════════════════════
1249+ // PendingAction reasonStr
1250+ // ═══════════════════════════════════════════════════════════════════════════════
1251+
1252+ test "Policy — PendingAction reasonStr empty" {
1253+ const item = PendingAction {};
1254+ try std .testing .expectEqualStrings ("" , item .reasonStr ());
1255+ }
1256+
1257+ test "Policy — PendingAction reasonStr with text" {
1258+ var item = PendingAction {};
1259+ const text = "need approval" ;
1260+ item .setReason (text );
1261+
1262+ try std .testing .expectEqualStrings (text , item .reasonStr ());
1263+ }
1264+
1265+ test "Policy — PendingAction setDetail truncates" {
1266+ var item = PendingAction {};
1267+ const long_text = [1 ]u8 {'X' } ** 256 ;
1268+ item .setReason (& long_text );
1269+
1270+ try std .testing .expectEqual (@as (u8 , 128 ), item .reason_len );
1271+ }
1272+
1273+ // ═══════════════════════════════════════════════════════════════════════════════
1274+ // ActionCounters window edge cases
1275+ // ═══════════════════════════════════════════════════════════════════════════════
1276+
1277+ test "Policy — ActionCounters window exactly 3600 sec" {
1278+ var c = ActionCounters {};
1279+ const now : i64 = 1234567890 ;
1280+ c .windows [0 ].start_ts = now - 3600 ;
1281+ c .windows [0 ].count = 5 ;
1282+ c .windows [1 ].start_ts = now ;
1283+ c .windows [1 ].count = 3 ;
1284+
1285+ const total = c .getCount (.farm_status );
1286+ try std .testing .expectEqual (@as (u8 , 3 ), total ); // Only recent window
1287+ }
1288+
1289+ test "Policy — ActionCounters getLastTs returns latest" {
1290+ var c = ActionCounters {};
1291+ c .windows [0 ].last_ts = 1000 ;
1292+ c .windows [1 ].last_ts = 2000 ;
1293+
1294+ try std .testing .expectEqual (@as (i64 , 2000 ), c .getLastTs (.doctor_quick ));
1295+ }
1296+
1297+ test "Policy — ActionCounters record updates last_ts" {
1298+ var c = ActionCounters {};
1299+ const now : i64 = 999999 ;
1300+ c .record (.farm_recycle , now );
1301+
1302+ try std .testing .expectEqual (@as (i64 , 999999 ), c .getLastTs (.farm_recycle ));
1303+ }
1304+
1305+ // ═══════════════════════════════════════════════════════════════════════════════
1306+ // IncidentMemory lastN full buffer
1307+ // ═══════════════════════════════════════════════════════════════════════════════
1308+
1309+ test "Policy — IncidentMemory lastN returns oldest first" {
1310+ var m = IncidentMemory .init ();
1311+ m .record (.alert , .doctor_quick , true , "first" );
1312+ m .record (.auto_action , .farm_recycle , true , "second" );
1313+ m .record (.escalation , .farm_evolve , true , "third" );
1314+
1315+ var buf : [MAX_INCIDENTS ]Incident = undefined ;
1316+ const count = m .lastN (& buf );
1317+
1318+ try std .testing .expectEqual (@as (u32 , 3 ), count );
1319+ // Oldest should be first
1320+ try std .testing .expectEqualStrings ("first" , buf [0 ].detailStr ());
1321+ }
1322+
1323+ test "Policy — IncidentMemory lastN respects ring wrap" {
1324+ var m = IncidentMemory .init ();
1325+ // Fill buffer to cause wrap
1326+ var i : u32 = 0 ;
1327+ while (i < MAX_INCIDENTS + 5 ) : (i += 1 ) {
1328+ m .record (.alert , .doctor_quick , true , "test" );
1329+ }
1330+
1331+ var buf : [MAX_INCIDENTS ]Incident = undefined ;
1332+ const count = m .lastN (& buf );
1333+
1334+ try std .testing .expectEqual (@as (u32 , MAX_INCIDENTS ), count );
1335+ }
1336+
1337+ // ═══════════════════════════════════════════════════════════════════════════════
1338+ // PendingQueue edge cases
1339+ // ═══════════════════════════════════════════════════════════════════════════════
1340+
1341+ test "Policy — PendingQueue expireOld all expired" {
1342+ var q = PendingQueue .init ();
1343+ _ = q .add (.doctor_quick , "test1" );
1344+ _ = q .add (.farm_status , "test2" );
1345+
1346+ // Age all items
1347+ const now = std .time .timestamp ();
1348+ for (& q .items ) | * item | {
1349+ if (item .active ) {
1350+ item .requested_at = now - 2000 ;
1351+ }
1352+ }
1353+
1354+ q .expireOld ();
1355+ try std .testing .expectEqual (@as (u8 , 0 ), q .pendingCount ());
1356+ }
1357+
1358+ test "Policy — PendingQueue expireOld keeps recent" {
1359+ var q = PendingQueue .init ();
1360+ _ = q .add (.doctor_quick , "test" );
1361+
1362+ q .expireOld ();
1363+ try std .testing .expectEqual (@as (u8 , 1 ), q .pendingCount ());
1364+ }
1365+
1366+ // ═══════════════════════════════════════════════════════════════════════════════
1367+ // PolicyVerdict reason texts
1368+ // ═══════════════════════════════════════════════════════════════════════════════
1369+
1370+ test "Policy — PolicyVerdict reason texts" {
1371+ try std .testing .expectEqualStrings ("allowed" , .allowed .reason ());
1372+ try std .testing .expectEqualStrings ("level" , .denied_level .reason ());
1373+ try std .testing .expectEqualStrings ("rate" , .denied_rate .reason ());
1374+ try std .testing .expectEqualStrings ("cooldown" , .denied_cooldown .reason ());
1375+ try std .testing .expectEqualStrings ("escalated" , .denied_escalated .reason ());
1376+ try std .testing .expectEqualStrings ("approval" , .needs_approval .reason ());
1377+ }
0 commit comments