返回 CodeWhale
conformance_tests.rs
根目录 / crates / config / src / route / conformance_tests.rs
1 //! Provider descriptor conformance (#3084).
2 //!
3 //! These tests assert that *every* shipped `ProviderKind` has a well-formed
4 //! route-facing descriptor and resolves a default route — so adding a provider
5 //! without wiring its descriptor/resolver behavior fails CI here rather than at
6 //! runtime. They are intentionally data-driven over [`ProviderKind::all`] and
7 //! network-free; provider execution/adapter behavior is exercised elsewhere.
8
9 use super::bundled_offerings;
10 use super::descriptor::ProviderDescriptor;
11 use super::ids::{LogicalModelRef, ProviderId};
12 use super::resolver::{RouteRequest, RouteResolver};
13 use crate::ProviderKind;
14
15 fn none_request(kind: ProviderKind) -> RouteRequest {
16 RouteRequest {
17 explicit_provider: Some(kind),
18 model_selector: None,
19 saved_provider_model: None,
20 base_url_override: None,
21 limit_overrides: Vec::new(),
22 }
23 }
24
25 #[test]
26 fn every_provider_kind_has_a_wellformed_descriptor() {
27 for &kind in ProviderKind::all() {
28 let descriptor = ProviderDescriptor::for_kind(kind);
29
30 // The descriptor id is non-empty and agrees with the canonical mapping;
31 // a mismatch means a provider was added to one table but not the other.
32 assert!(
33 !descriptor.id().as_str().trim().is_empty(),
34 "{kind:?}: empty provider id"
35 );
36 assert_eq!(
37 descriptor.id(),
38 ProviderId::from_kind(kind),
39 "{kind:?}: descriptor id disagrees with ProviderId::from_kind"
40 );
41
42 // Transport facts route resolution depends on must be present.
43 assert!(
44 !descriptor.default_wire_model().as_str().trim().is_empty(),
45 "{kind:?}: empty default wire model"
46 );
47 assert!(
48 !descriptor.default_base_url().trim().is_empty(),
49 "{kind:?}: empty default base URL"
50 );
51
52 // Any declared auth env var name must be a real, non-empty key.
53 for env_var in descriptor.env_vars() {
54 assert!(
55 !env_var.trim().is_empty(),
56 "{kind:?}: empty env var name in descriptor"
57 );
58 }
59
60 // Current fixed providers resolve a concrete protocol for their
61 // default endpoint key.
62 let _ = descriptor.protocol_for_endpoint("chat");
63 }
64 }
65
66 #[test]
67 fn every_provider_kind_resolves_its_default_route() {
68 let resolver = RouteResolver::new();
69 let bundled = bundled_offerings();
70 for &kind in ProviderKind::all() {
71 let descriptor = ProviderDescriptor::for_kind(kind);
72 let candidate = resolver.resolve(&none_request(kind)).unwrap_or_else(|err| {
73 panic!("{kind:?}: default (None selector) route must resolve, got {err:?}")
74 });
75
76 assert_eq!(
77 candidate.provider_kind(),
78 kind,
79 "{kind:?}: resolved to a different provider"
80 );
81 assert_eq!(
82 candidate.provider_id(),
83 &ProviderId::from_kind(kind),
84 "{kind:?}: resolved provider id mismatch"
85 );
86
87 // The resolver prefers this provider's bundled *default offering* wire id
88 // when one exists, and otherwise falls back to the descriptor default
89 // wire model. Assert that exact contract so a future drift between the
90 // catalog-backed default and `Provider::default_model()` fails with an
91 // honest message instead of coincidentally passing.
92 let expected_wire = bundled
93 .iter()
94 .find(|offering| {
95 offering.provider == ProviderId::from_kind(kind) && offering.default_for_provider
96 })
97 .map_or_else(
98 || descriptor.default_wire_model().as_str().to_string(),
99 |offering| offering.wire_model_id.as_str().to_string(),
100 );
101 assert_eq!(
102 candidate.wire_model_id().as_str(),
103 expected_wire,
104 "{kind:?}: None selector must resolve to the bundled default offering (or descriptor default)"
105 );
106 }
107 }
108
109 #[test]
110 fn every_provider_kind_resolves_the_auto_selector() {
111 let resolver = RouteResolver::new();
112 for &kind in ProviderKind::all() {
113 let request = RouteRequest {
114 explicit_provider: Some(kind),
115 model_selector: Some(LogicalModelRef::from("auto")),
116 saved_provider_model: None,
117 base_url_override: None,
118 limit_overrides: Vec::new(),
119 };
120 let candidate = resolver
121 .resolve(&request)
122 .unwrap_or_else(|err| panic!("{kind:?}: `auto` must resolve, got {err:?}"));
123
124 assert_eq!(
125 candidate.provider_kind(),
126 kind,
127 "{kind:?}: auto resolved to a different provider"
128 );
129 assert!(
130 candidate.logical_model().is_auto(),
131 "{kind:?}: `auto` must stay the auto sentinel, never a literal model"
132 );
133 // `auto` with no catalog default falls back to the descriptor default,
134 // which conformance #2 already pins; here we only assert it resolves.
135 assert!(
136 !candidate.wire_model_id().as_str().trim().is_empty(),
137 "{kind:?}: auto resolved to an empty wire model"
138 );
139 }
140 }
141
142 #[test]
143 fn modelstudio_qwen38_max_offering_publishes_1m_context() {
144 use super::RouteLimits;
145
146 let offering = bundled_offerings()
147 .into_iter()
148 .find(|offering| {
149 offering.provider.as_str() == "modelstudio-token-plan"
150 && offering.wire_model_id.as_str() == "qwen3.8-max"
151 })
152 .expect("modelstudio qwen3.8-max offering");
153 assert_eq!(
154 offering.limits,
155 RouteLimits {
156 context_tokens: Some(1_000_000),
157 input_tokens: None,
158 output_tokens: Some(131_072),
159 },
160 "hand-seam limits must not be empty (empty won collisions and fell to 128K)"
161 );
162 }
163
164 #[test]
165 fn modelstudio_image_input_capability_is_per_model() {
166 use super::capabilities::CapabilityState;
167
168 // Owner's Token Plan console (verified 2026-08-03) lists Visual
169 // Understanding for exactly these four models; upstream Models.dev
170 // modalities agree (image/video input) and mark the rest text-only.
171 const VISION: &[&str] = &[
172 "qwen3.8-max",
173 "qwen3.8-max-preview",
174 "qwen3.7-plus",
175 "qwen3.6-flash",
176 ];
177 const TEXT_ONLY: &[&str] = &[
178 "qwen3.7-max",
179 "deepseek-v4-pro",
180 "deepseek-v4-flash-0731",
181 "glm-5.2",
182 ];
183 // Catalog surface is one vendor identity; plan/dialect are config.
184 const PROVIDERS: &[&str] = &["modelstudio-token-plan"];
185
186 let offerings = bundled_offerings();
187 for provider in PROVIDERS {
188 for (models, expected) in [
189 (VISION, CapabilityState::Supported),
190 (TEXT_ONLY, CapabilityState::Unsupported),
191 ] {
192 for model in models {
193 let offering = offerings
194 .iter()
195 .find(|offering| {
196 offering.provider.as_str() == *provider
197 && offering.wire_model_id.as_str() == *model
198 })
199 .unwrap_or_else(|| panic!("{provider}/{model}: missing bundled offering"));
200 assert_eq!(
201 offering.capabilities.image_input, expected,
202 "{provider}/{model}: image_input drifted from the console-verified capability"
203 );
204 }
205 }
206 }
207 }
208
208 lines RUST