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