@@ -2019,3 +2019,226 @@ test "Queen telegram — CommandQueue new queue empty" {
20192019
20202020 try std .testing .expectEqual (@as (? TgCommand , null ), q .pop ());
20212021}
2022+
2023+ // ═══════════════════════════════════════════════════════════════════════════════
2024+ // REAL FUNCTION TESTS — Actual computation, return values, data processing
2025+ // ═══════════════════════════════════════════════════════════════════════════════
2026+
2027+ test "Queen telegram — parseActionKind returns correct enum for all 29 actions" {
2028+ // Verify each action maps to the correct enum value
2029+ const tests = .{
2030+ .{ "farm_status" , qt .ActionKind .farm_status },
2031+ .{ "arena_status" , qt .ActionKind .arena_status },
2032+ .{ "doctor_scan" , qt .ActionKind .doctor_scan },
2033+ .{ "train_status" , qt .ActionKind .train_status },
2034+ .{ "train_diagnose" , qt .ActionKind .train_diagnose },
2035+ .{ "experiment_chart" , qt .ActionKind .experiment_chart },
2036+ .{ "patent_status" , qt .ActionKind .patent_status },
2037+ .{ "research_sacred" , qt .ActionKind .research_sacred },
2038+ .{ "ouroboros_status" , qt .ActionKind .ouroboros_status },
2039+ .{ "experience_recall" , qt .ActionKind .experience_recall },
2040+ .{ "farm_evolve_status" , qt .ActionKind .farm_evolve_status },
2041+ .{ "swarm_status" , qt .ActionKind .swarm_status },
2042+ .{ "doctor_quick" , qt .ActionKind .doctor_quick },
2043+ .{ "doctor_heal" , qt .ActionKind .doctor_heal },
2044+ .{ "ouroboros_cycle" , qt .ActionKind .ouroboros_cycle },
2045+ .{ "git_commit" , qt .ActionKind .git_commit_state },
2046+ .{ "git_push" , qt .ActionKind .git_push },
2047+ .{ "issue_comment" , qt .ActionKind .issue_comment },
2048+ .{ "notify" , qt .ActionKind .notify },
2049+ .{ "arena_battle" , qt .ActionKind .arena_battle },
2050+ .{ "experience_save" , qt .ActionKind .experience_save },
2051+ .{ "fmt" , qt .ActionKind .fmt },
2052+ .{ "farm_recycle" , qt .ActionKind .farm_recycle },
2053+ .{ "farm_evolve_step" , qt .ActionKind .farm_evolve_step },
2054+ .{ "cloud_spawn" , qt .ActionKind .cloud_spawn },
2055+ .{ "cloud_kill" , qt .ActionKind .cloud_kill },
2056+ .{ "cloud_cleanup" , qt .ActionKind .cloud_cleanup },
2057+ .{ "issue_create" , qt .ActionKind .issue_create },
2058+ .{ "swarm_decompose" , qt .ActionKind .swarm_decompose },
2059+ };
2060+
2061+ inline for (tests ) | t | {
2062+ const result = parseActionKind (t [0 ]);
2063+ try std .testing .expect (result != null );
2064+ try std .testing .expectEqual (t [1 ], result .? );
2065+ }
2066+ }
2067+
2068+ test "Queen telegram — parseActionKind returns null for invalid inputs" {
2069+ // Test that parseActionKind correctly returns null for invalid strings
2070+ const invalid_inputs = .{
2071+ "" ,
2072+ " " ,
2073+ "unknown_action" ,
2074+ "FARM_STATUS" , // uppercase
2075+ "farm_status " , // trailing space
2076+ " farm_status" , // leading space
2077+ "farm-status" , // wrong separator
2078+ "farmstatus" , // missing separator
2079+ "arena" , // prefix only
2080+ "doctor_" , // incomplete
2081+ };
2082+
2083+ inline for (invalid_inputs ) | input | {
2084+ const result = parseActionKind (input );
2085+ try std .testing .expectEqual (@as (? qt .ActionKind , null ), result );
2086+ }
2087+ }
2088+
2089+ test "Queen telegram — fmtActionResult calculates preview_len correctly" {
2090+ // Verify that fmtActionResult truncates output to preview_len = @min(output_len, 400)
2091+ var buf : [2048 ]u8 = undefined ;
2092+
2093+ // Test with output < 400 chars (should use full output)
2094+ var short_result = qt.ActionResult {
2095+ .success = true ,
2096+ .duration_ms = 100 ,
2097+ };
2098+ const short_text = "short output" ;
2099+ @memcpy (short_result .output [0.. short_text .len ], short_text );
2100+ short_result .output_len = short_text .len ;
2101+
2102+ const short_msg = fmtActionResult (& buf , .doctor_quick , short_result );
2103+ try std .testing .expect (std .mem .indexOf (u8 , short_msg , short_text ) != null );
2104+
2105+ // Test with output > 400 chars (should truncate)
2106+ var long_result = qt.ActionResult {
2107+ .success = true ,
2108+ .duration_ms = 100 ,
2109+ };
2110+ const long_text = [1 ]u8 {'X' } ** 500 ;
2111+ @memcpy (long_result .output [0.. long_text .len ], & long_text );
2112+ long_result .output_len = long_text .len ;
2113+
2114+ const long_msg = fmtActionResult (& buf , .doctor_quick , long_result );
2115+ // Count X's in output - should be at most 400 (but may be less due to truncation)
2116+ var x_count : usize = 0 ;
2117+ for (long_msg ) | c | {
2118+ if (c == 'X' ) x_count += 1 ;
2119+ }
2120+ // The preview should be truncated to 400 chars max
2121+ try std .testing .expect (x_count <= 400 );
2122+ }
2123+
2124+ test "Queen telegram — CommandQueue push returns boolean indicating capacity" {
2125+ var q = CommandQueue {};
2126+
2127+ // First MAX_COMMANDS-1 pushes should succeed
2128+ var i : u32 = 0 ;
2129+ while (i < MAX_COMMANDS - 1 ) : (i += 1 ) {
2130+ const result = q .push (.{ .update_id = @as (i64 , i ) });
2131+ try std .testing .expectEqual (true , result );
2132+ }
2133+
2134+ // Next push should fail (queue full)
2135+ const full_result = q .push (.{ .update_id = 999 });
2136+ try std .testing .expectEqual (false , full_result );
2137+
2138+ // Pop one item
2139+ _ = q .pop ();
2140+
2141+ // Push should succeed again
2142+ const after_pop_result = q .push (.{ .update_id = 1000 });
2143+ try std .testing .expectEqual (true , after_pop_result );
2144+ }
2145+
2146+ test "Queen telegram — CommandQueue pop returns optional with correct state" {
2147+ var q = CommandQueue {};
2148+
2149+ // Pop on empty queue should return null
2150+ const empty_result = q .pop ();
2151+ try std .testing .expectEqual (@as (? TgCommand , null ), empty_result );
2152+
2153+ // Push and verify pop returns the command
2154+ const test_cmd = TgCommand { .update_id = 42 };
2155+ _ = q .push (test_cmd );
2156+
2157+ const popped = q .pop ();
2158+ try std .testing .expect (popped != null );
2159+ try std .testing .expectEqual (@as (i64 , 42 ), popped .? .update_id );
2160+
2161+ // Pop again should return null
2162+ const after_result = q .pop ();
2163+ try std .testing .expectEqual (@as (? TgCommand , null ), after_result );
2164+ }
2165+
2166+ test "Queen telegram — TgCommand textStr returns correct slice" {
2167+ var cmd = TgCommand {};
2168+
2169+ // Test empty text
2170+ cmd .text_len = 0 ;
2171+ try std .testing .expectEqualStrings ("" , cmd .textStr ());
2172+ try std .testing .expectEqual (@as (usize , 0 ), cmd .textStr ().len );
2173+
2174+ // Test with content
2175+ const text = "/queen act farm_recycle" ;
2176+ @memcpy (cmd .text [0.. text .len ], text );
2177+ cmd .text_len = text .len ;
2178+
2179+ const result = cmd .textStr ();
2180+ try std .testing .expectEqualStrings (text , result );
2181+ try std .testing .expectEqual (@as (usize , text .len ), result .len );
2182+ try std .testing .expectEqual (@as (usize , text .len ), cmd .text_len );
2183+
2184+ // Verify result points to cmd.text array
2185+ try std .testing .expectEqual (@as ([* ]const u8 , @ptrCast (& cmd .text )), result .ptr );
2186+ }
2187+
2188+ test "Queen telegram — fmtActionResult includes correct success/failure status" {
2189+ var buf : [2048 ]u8 = undefined ;
2190+
2191+ // Test success case
2192+ var success_result = qt.ActionResult {
2193+ .success = true ,
2194+ .duration_ms = 50 ,
2195+ };
2196+ const success_output = "all good" ;
2197+ @memcpy (success_result .output [0.. success_output .len ], success_output );
2198+ success_result .output_len = success_output .len ;
2199+
2200+ const success_msg = fmtActionResult (& buf , .doctor_quick , success_result );
2201+ try std .testing .expect (std .mem .indexOf (u8 , success_msg , "OK" ) != null );
2202+ try std .testing .expect (std .mem .indexOf (u8 , success_msg , "FAIL" ) == null );
2203+
2204+ // Test failure case
2205+ var failure_result = qt.ActionResult {
2206+ .success = false ,
2207+ .duration_ms = 50 ,
2208+ };
2209+ const fail_output = "error occurred" ;
2210+ @memcpy (failure_result .output [0.. fail_output .len ], fail_output );
2211+ failure_result .output_len = fail_output .len ;
2212+
2213+ const failure_msg = fmtActionResult (& buf , .farm_recycle , failure_result );
2214+ try std .testing .expect (std .mem .indexOf (u8 , failure_msg , "FAIL" ) != null );
2215+ try std .testing .expect (std .mem .indexOf (u8 , failure_msg , "OK" ) == null );
2216+ }
2217+
2218+ test "Queen telegram — fmtActionResult formats duration correctly" {
2219+ var buf : [2048 ]u8 = undefined ;
2220+ var result = qt.ActionResult {
2221+ .success = true ,
2222+ .duration_ms = 12345 ,
2223+ };
2224+ const output = "test" ;
2225+ @memcpy (result .output [0.. output .len ], output );
2226+ result .output_len = output .len ;
2227+
2228+ const msg = fmtActionResult (& buf , .doctor_quick , result );
2229+
2230+ // Verify duration is in the message
2231+ try std .testing .expect (std .mem .indexOf (u8 , msg , "12345" ) != null );
2232+ try std .testing .expect (std .mem .indexOf (u8 , msg , "ms" ) != null );
2233+ }
2234+
2235+ test "Queen telegram — parseActionKind handles substring matching correctly" {
2236+ // Verify exact matching only (no prefix/suffix matches)
2237+ try std .testing .expectEqual (qt .ActionKind .farm_status , parseActionKind ("farm_status" ).? );
2238+
2239+ // These should NOT match (different strings)
2240+ try std .testing .expectEqual (@as (? qt .ActionKind , null ), parseActionKind ("farm_statuses" ));
2241+ try std .testing .expectEqual (@as (? qt .ActionKind , null ), parseActionKind ("farm_status_extra" ));
2242+ try std .testing .expectEqual (@as (? qt .ActionKind , null ), parseActionKind ("my_farm_status" ));
2243+ try std .testing .expectEqual (@as (? qt .ActionKind , null ), parseActionKind ("farm" ));
2244+ }
0 commit comments