Skip to content

Commit 62a9ae0

Browse files
authored
add basic versioned tests (#4)
Tests for evolution and failure modes are upcoming.
1 parent adc07c2 commit 62a9ae0

File tree

4 files changed

+753
-2
lines changed

4 files changed

+753
-2
lines changed

crates/integration-tests/src/common/fixtures.rs

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,297 @@ pub struct UpdateUserRequest {
157157
pub name: Option<String>,
158158
pub email: Option<String>,
159159
}
160+
161+
/// Versioned health API for testing version evolution.
162+
pub mod versioned_health {
163+
use super::*;
164+
use dropshot_api_manager_types::api_versions;
165+
166+
api_versions!(
167+
[(3, WITH_METRICS), (2, WITH_DETAILED_STATUS), (1, INITIAL),]
168+
);
169+
170+
#[dropshot::api_description]
171+
pub trait VersionedHealthApi {
172+
type Context;
173+
174+
/// Check if the service is healthy (all versions).
175+
#[endpoint {
176+
method = GET,
177+
path = "/health",
178+
operation_id = "health_check",
179+
versions = "1.0.0"..
180+
}]
181+
async fn health_check(
182+
rqctx: RequestContext<Self::Context>,
183+
) -> Result<HttpResponseOk<HealthStatusV1>, HttpError>;
184+
185+
/// Get detailed health status (v2+).
186+
#[endpoint {
187+
method = GET,
188+
path = "/health/detailed",
189+
operation_id = "detailed_health_check",
190+
versions = "2.0.0"..
191+
}]
192+
async fn detailed_health_check(
193+
rqctx: RequestContext<Self::Context>,
194+
) -> Result<HttpResponseOk<DetailedHealthStatus>, HttpError>;
195+
196+
/// Get service metrics (v3+).
197+
#[endpoint {
198+
method = GET,
199+
path = "/metrics",
200+
operation_id = "get_metrics",
201+
versions = "3.0.0"..
202+
}]
203+
async fn get_metrics(
204+
rqctx: RequestContext<Self::Context>,
205+
) -> Result<HttpResponseOk<ServiceMetrics>, HttpError>;
206+
}
207+
208+
/// Basic health status response (v1).
209+
#[derive(JsonSchema, Serialize)]
210+
pub struct HealthStatusV1 {
211+
pub status: String,
212+
pub timestamp: DateTime<Utc>,
213+
}
214+
215+
/// Detailed health status response (v2+).
216+
#[derive(JsonSchema, Serialize)]
217+
pub struct DetailedHealthStatus {
218+
pub status: String,
219+
pub timestamp: DateTime<Utc>,
220+
pub uptime_seconds: u64,
221+
pub dependencies: Vec<DependencyStatus>,
222+
}
223+
224+
/// Dependency status information.
225+
#[derive(JsonSchema, Serialize)]
226+
pub struct DependencyStatus {
227+
pub name: String,
228+
pub status: String,
229+
pub response_time_ms: Option<u64>,
230+
}
231+
232+
/// Service metrics response (v3+).
233+
#[derive(JsonSchema, Serialize)]
234+
pub struct ServiceMetrics {
235+
pub requests_per_second: f64,
236+
pub error_rate: f64,
237+
pub avg_response_time_ms: f64,
238+
pub active_connections: u32,
239+
}
240+
}
241+
242+
/// Versioned user API for testing complex schema evolution.
243+
pub mod versioned_user {
244+
use super::*;
245+
use dropshot_api_manager_types::api_versions;
246+
247+
api_versions!([
248+
(3, WITH_ROLES_AND_PERMISSIONS),
249+
(2, WITH_PROFILE_DATA),
250+
(1, INITIAL),
251+
]);
252+
253+
#[dropshot::api_description]
254+
pub trait VersionedUserApi {
255+
type Context;
256+
257+
/// List all users (all versions).
258+
#[endpoint {
259+
method = GET,
260+
path = "/users",
261+
operation_id = "list_users",
262+
versions = "1.0.0"..VERSION_WITH_PROFILE_DATA
263+
}]
264+
async fn list_users_v1(
265+
rqctx: RequestContext<Self::Context>,
266+
query: Query<ListUsersQueryV1>,
267+
) -> Result<HttpResponseOk<UserListV1>, HttpError>;
268+
269+
/// List all users with profile data (v2+).
270+
#[endpoint {
271+
method = GET,
272+
path = "/users",
273+
operation_id = "list_users",
274+
versions = "2.0.0"..
275+
}]
276+
async fn list_users_v2(
277+
rqctx: RequestContext<Self::Context>,
278+
query: Query<ListUsersQueryV2>,
279+
) -> Result<HttpResponseOk<UserListV2>, HttpError>;
280+
281+
/// Get a specific user by ID (all versions).
282+
#[endpoint {
283+
method = GET,
284+
path = "/users/{id}",
285+
operation_id = "get_user",
286+
versions = "1.0.0"..VERSION_WITH_PROFILE_DATA
287+
}]
288+
async fn get_user_v1(
289+
rqctx: RequestContext<Self::Context>,
290+
path: Path<UserIdPath>,
291+
) -> Result<HttpResponseOk<UserV1>, HttpError>;
292+
293+
/// Get a specific user by ID with profile data (v2+).
294+
#[endpoint {
295+
method = GET,
296+
path = "/users/{id}",
297+
operation_id = "get_user",
298+
versions = "2.0.0"..
299+
}]
300+
async fn get_user_v2(
301+
rqctx: RequestContext<Self::Context>,
302+
path: Path<UserIdPath>,
303+
) -> Result<HttpResponseOk<UserV2>, HttpError>;
304+
305+
/// Create a new user (v1 only).
306+
#[endpoint {
307+
method = POST,
308+
path = "/users",
309+
operation_id = "create_user",
310+
versions = "1.0.0"..VERSION_WITH_PROFILE_DATA
311+
}]
312+
async fn create_user_v1(
313+
rqctx: RequestContext<Self::Context>,
314+
body: TypedBody<CreateUserRequestV1>,
315+
) -> Result<HttpResponseOk<UserV1>, HttpError>;
316+
317+
/// Create a new user with profile data (v2+).
318+
#[endpoint {
319+
method = POST,
320+
path = "/users",
321+
operation_id = "create_user",
322+
versions = "2.0.0"..
323+
}]
324+
async fn create_user_v2(
325+
rqctx: RequestContext<Self::Context>,
326+
body: TypedBody<CreateUserRequestV2>,
327+
) -> Result<HttpResponseOk<UserV2>, HttpError>;
328+
329+
/// Assign role to user (v3+).
330+
#[endpoint {
331+
method = PUT,
332+
path = "/users/{id}/role",
333+
operation_id = "assign_user_role",
334+
versions = "3.0.0"..
335+
}]
336+
async fn assign_user_role(
337+
rqctx: RequestContext<Self::Context>,
338+
path: Path<UserIdPath>,
339+
body: TypedBody<AssignRoleRequest>,
340+
) -> Result<HttpResponseOk<UserV2>, HttpError>;
341+
342+
/// List user permissions (v3+).
343+
#[endpoint {
344+
method = GET,
345+
path = "/users/{id}/permissions",
346+
operation_id = "list_user_permissions",
347+
versions = "3.0.0"..
348+
}]
349+
async fn list_user_permissions(
350+
rqctx: RequestContext<Self::Context>,
351+
path: Path<UserIdPath>,
352+
) -> Result<HttpResponseOk<UserPermissions>, HttpError>;
353+
}
354+
355+
/// Query parameters for listing users (v1).
356+
#[derive(JsonSchema, Deserialize)]
357+
pub struct ListUsersQueryV1 {
358+
pub limit: Option<u32>,
359+
pub offset: Option<u32>,
360+
}
361+
362+
/// Query parameters for listing users (v2+).
363+
#[derive(JsonSchema, Deserialize)]
364+
pub struct ListUsersQueryV2 {
365+
pub limit: Option<u32>,
366+
pub offset: Option<u32>,
367+
pub email_filter: Option<String>,
368+
pub include_inactive: Option<bool>,
369+
}
370+
371+
/// User information (v1).
372+
#[derive(JsonSchema, Serialize)]
373+
pub struct UserV1 {
374+
pub id: u32,
375+
pub name: String,
376+
pub email: String,
377+
pub created_at: DateTime<Utc>,
378+
}
379+
380+
/// User information with profile data (v2+).
381+
#[derive(JsonSchema, Serialize)]
382+
pub struct UserV2 {
383+
pub id: u32,
384+
pub name: String,
385+
pub email: String,
386+
pub created_at: DateTime<Utc>,
387+
pub updated_at: DateTime<Utc>,
388+
pub profile: UserProfile,
389+
pub is_active: bool,
390+
pub role: Option<String>, // Added in v3 but wire-compatible.
391+
}
392+
393+
/// User profile information (v2+).
394+
#[derive(JsonSchema, Serialize)]
395+
pub struct UserProfile {
396+
pub bio: Option<String>,
397+
pub avatar_url: Option<String>,
398+
pub timezone: Option<String>,
399+
pub preferred_language: Option<String>,
400+
}
401+
402+
/// List of users response (v1).
403+
#[derive(JsonSchema, Serialize)]
404+
pub struct UserListV1 {
405+
pub users: Vec<UserV1>,
406+
pub total_count: u32,
407+
}
408+
409+
/// List of users response (v2+).
410+
#[derive(JsonSchema, Serialize)]
411+
pub struct UserListV2 {
412+
pub users: Vec<UserV2>,
413+
pub total_count: u32,
414+
pub has_more: bool,
415+
}
416+
417+
/// Request to create a new user (v1).
418+
#[derive(JsonSchema, Deserialize)]
419+
pub struct CreateUserRequestV1 {
420+
pub name: String,
421+
pub email: String,
422+
}
423+
424+
/// Request to create a new user with profile (v2+).
425+
#[derive(JsonSchema, Deserialize)]
426+
pub struct CreateUserRequestV2 {
427+
pub name: String,
428+
pub email: String,
429+
pub profile: Option<CreateUserProfile>,
430+
}
431+
432+
/// Profile data for user creation (v2+).
433+
#[derive(JsonSchema, Deserialize)]
434+
pub struct CreateUserProfile {
435+
pub bio: Option<String>,
436+
pub timezone: Option<String>,
437+
pub preferred_language: Option<String>,
438+
}
439+
440+
/// Request to assign role to user (v3+).
441+
#[derive(JsonSchema, Deserialize)]
442+
pub struct AssignRoleRequest {
443+
pub role: String,
444+
}
445+
446+
/// User permissions response (v3+).
447+
#[derive(JsonSchema, Serialize)]
448+
pub struct UserPermissions {
449+
pub user_id: u32,
450+
pub role: String,
451+
pub permissions: Vec<String>,
452+
}
453+
}

0 commit comments

Comments
 (0)