| 1 | //! Untouched-source parity samples for the diagnostics slice (FEAT-029). |
| 2 | //! |
| 3 | //! The checked-in fixture was captured on baseline 922679d6c. This test only |
| 4 | //! reads it; it never updates expected bytes. Timestamped reports and seeded |
| 5 | //! pricing/telemetry are captured separately in debug_diagnostics_baseline_tests; |
| 6 | //! both suites must pass again after the handlers move. |
| 7 | |
| 8 | use super::{CommandResult, execute}; |
| 9 | use crate::config::Config; |
| 10 | use crate::tui::app::{App, AppAction}; |
| 11 | use codewhale_models::{SystemBlock, SystemPrompt}; |
| 12 | use serde_json::{Value, json}; |
| 13 | |
| 14 | fn outcome(result: CommandResult) -> Value { |
| 15 | let action = match result.action { |
| 16 | None => Value::Null, |
| 17 | Some(AppAction::FetchBalance) => json!({"type": "FetchBalance"}), |
| 18 | Some(AppAction::CacheWarmup) => json!({"type": "CacheWarmup"}), |
| 19 | Some(AppAction::OpenContextInspector) => json!({"type": "OpenContextInspector"}), |
| 20 | Some(AppAction::OpenTextPager { title, content }) => { |
| 21 | json!({"type": "OpenTextPager", "title": title, "content": content}) |
| 22 | } |
| 23 | Some(AppAction::PreviewOutboundRequest { |
| 24 | json, |
| 25 | base_prompt_only, |
| 26 | hypothetical_prompt, |
| 27 | }) => json!({"type": "PreviewOutboundRequest", "json": json, |
| 28 | "base_prompt_only": base_prompt_only, "hypothetical_prompt": hypothetical_prompt}), |
| 29 | Some(other) => panic!("unexpected diagnostics action: {other:?}"), |
| 30 | }; |
| 31 | json!({"message": result.message, "action": action, "is_error": result.is_error}) |
| 32 | } |
| 33 | |
| 34 | fn collect() -> Value { |
| 35 | let cases = [ |
| 36 | "/balance", |
| 37 | "/tokens", |
| 38 | "/cost", |
| 39 | "/cache", |
| 40 | "/cache 0", |
| 41 | "/cache stats", |
| 42 | "/cache zones", |
| 43 | "/cache inspect --verbose --json", |
| 44 | "/cache warmup", |
| 45 | "/cache nonsense", |
| 46 | "/preview-request", |
| 47 | "/dryrun json", |
| 48 | "/preview_request --base-prompt", |
| 49 | "/preview-request --json --prompt keep trailing ", |
| 50 | "/preview-request --base-prompt --json", |
| 51 | "/preview-request --prompt", |
| 52 | "/tools", |
| 53 | "/tool-studio invalid", |
| 54 | "/system", |
| 55 | "/xitong", |
| 56 | "/context", |
| 57 | "/ctx nonsense", |
| 58 | ]; |
| 59 | let mut samples = serde_json::Map::new(); |
| 60 | for command in cases { |
| 61 | // App::new already protects its own settings transaction. Never wrap it |
| 62 | // in with_test_state_io_lock: that mutex is non-reentrant. |
| 63 | let workspace = tempfile::tempdir().expect("owned fixture workspace"); |
| 64 | let mut app = App::new( |
| 65 | crate::test_support::test_tui_options(workspace.path()), |
| 66 | &Config::default(), |
| 67 | ); |
| 68 | app.ui_locale = codewhale_localization::Locale::En; |
| 69 | app.set_provider_identity_record( |
| 70 | crate::config::Config::default() |
| 71 | .resolve_provider_identity(crate::config::ProviderKind::Deepseek.as_str()) |
| 72 | .expect("captured fixture provider"), |
| 73 | ); |
| 74 | samples.insert(command.to_string(), outcome(execute(command, &mut app))); |
| 75 | } |
| 76 | for (label, prompt) in [ |
| 77 | ("text", SystemPrompt::Text("Example policy".into())), |
| 78 | ( |
| 79 | "utf8_boundary", |
| 80 | SystemPrompt::Text(format!("{}éEND", "a".repeat(499))), |
| 81 | ), |
| 82 | ] { |
| 83 | let workspace = tempfile::tempdir().expect("owned fixture workspace"); |
| 84 | let mut app = App::new( |
| 85 | crate::test_support::test_tui_options(workspace.path()), |
| 86 | &Config::default(), |
| 87 | ); |
| 88 | app.ui_locale = codewhale_localization::Locale::En; |
| 89 | app.system_prompt = Some(prompt); |
| 90 | samples.insert( |
| 91 | format!("/system:{label}"), |
| 92 | outcome(execute("/system", &mut app)), |
| 93 | ); |
| 94 | } |
| 95 | for (label, command) in [ |
| 96 | ("balance_unsupported", "/balance"), |
| 97 | ("tokens_reported_telemetry", "/tokens"), |
| 98 | ("cost_priced_zero", "/cost"), |
| 99 | ("cost_legacy_unknown", "/cost"), |
| 100 | ("system_blocks", "/system"), |
| 101 | ] { |
| 102 | let workspace = tempfile::tempdir().expect("owned fixture workspace"); |
| 103 | let mut app = App::new( |
| 104 | crate::test_support::test_tui_options(workspace.path()), |
| 105 | &Config::default(), |
| 106 | ); |
| 107 | app.ui_locale = codewhale_localization::Locale::En; |
| 108 | app.set_provider_identity_record( |
| 109 | crate::config::Config::default() |
| 110 | .resolve_provider_identity(crate::config::ProviderKind::Deepseek.as_str()) |
| 111 | .expect("captured fixture provider"), |
| 112 | ); |
| 113 | match label { |
| 114 | "balance_unsupported" => app.set_provider_identity_record( |
| 115 | crate::config::Config::default() |
| 116 | .resolve_provider_identity(crate::config::ProviderKind::OpenaiCodex.as_str()) |
| 117 | .expect("captured fixture provider"), |
| 118 | ), |
| 119 | "tokens_reported_telemetry" => { |
| 120 | app.session.total_tokens = 1234; |
| 121 | app.session.last_prompt_tokens = Some(100); |
| 122 | app.session.last_completion_tokens = Some(25); |
| 123 | app.session.last_prompt_cache_hit_tokens = Some(70); |
| 124 | app.session.last_prompt_cache_miss_tokens = Some(30); |
| 125 | app.session.total_cache_write_tokens = 250; |
| 126 | } |
| 127 | "cost_priced_zero" => app.session.cost_priced_turns = 1, |
| 128 | "cost_legacy_unknown" => app.session.cost_coverage_unknown_legacy = true, |
| 129 | "system_blocks" => { |
| 130 | app.system_prompt = Some(SystemPrompt::Blocks(vec![ |
| 131 | SystemBlock { |
| 132 | block_type: "text".into(), |
| 133 | text: "First block".into(), |
| 134 | cache_control: None, |
| 135 | }, |
| 136 | SystemBlock { |
| 137 | block_type: "text".into(), |
| 138 | text: "Second block".into(), |
| 139 | cache_control: None, |
| 140 | }, |
| 141 | ])); |
| 142 | } |
| 143 | _ => unreachable!("fixture case list is exhaustive"), |
| 144 | } |
| 145 | samples.insert(label.into(), outcome(execute(command, &mut app))); |
| 146 | } |
| 147 | Value::Object(samples) |
| 148 | } |
| 149 | |
| 150 | #[test] |
| 151 | fn stable_branches_match_untouched_baseline() { |
| 152 | let expected: Value = |
| 153 | serde_json::from_str(include_str!("fixtures/diagnostics/stable-branches.json")) |
| 154 | .expect("reviewed baseline fixture must parse"); |
| 155 | let actual = collect(); |
| 156 | assert_eq!(actual, expected, "diagnostics parity diverged"); |
| 157 | } |
| 158 |