Skip to main content

cloud_sdk_testkit/
response.rs

1//! Deterministic response fixture builders.
2
3use cloud_sdk::transport::StatusCode;
4
5use crate::{ActionFixture, FixtureBody, PaginationFixture, RateLimitFixture};
6
7/// Fixture response category.
8#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub enum FixtureKind {
10    /// Successful response without additional metadata.
11    Success,
12    /// Successful paginated response.
13    Pagination,
14    /// Action polling response.
15    Action,
16    /// Rate-limit response.
17    RateLimit,
18    /// Client or server error response.
19    Error,
20}
21
22/// Response fixture construction error.
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
24pub enum ResponseFixtureError {
25    /// Error fixtures require a `4xx` or `5xx` status.
26    NonErrorStatus,
27}
28
29impl_static_error!(ResponseFixtureError,
30    Self::NonErrorStatus => "error fixture requires an HTTP error status",
31);
32
33/// Provider-neutral response body plus optional interpreted metadata.
34#[derive(Clone, Copy, Debug, Eq, PartialEq)]
35pub struct ResponseFixture<'a> {
36    kind: FixtureKind,
37    status: StatusCode,
38    body: FixtureBody<'a>,
39    pagination: Option<PaginationFixture>,
40    action: Option<ActionFixture>,
41    rate_limit: Option<RateLimitFixture>,
42    content_type: Option<&'a str>,
43}
44
45impl<'a> ResponseFixture<'a> {
46    /// Creates a `200 OK` response.
47    #[must_use]
48    pub const fn success(body: FixtureBody<'a>) -> Self {
49        Self::new(FixtureKind::Success, StatusCode::OK, body)
50    }
51
52    /// Creates a `200 OK` paginated response.
53    #[must_use]
54    pub const fn paginated(body: FixtureBody<'a>, pagination: PaginationFixture) -> Self {
55        let mut fixture = Self::new(FixtureKind::Pagination, StatusCode::OK, body);
56        fixture.pagination = Some(pagination);
57        fixture
58    }
59
60    /// Creates a `200 OK` action response.
61    #[must_use]
62    pub const fn action(body: FixtureBody<'a>, action: ActionFixture) -> Self {
63        let mut fixture = Self::new(FixtureKind::Action, StatusCode::OK, body);
64        fixture.action = Some(action);
65        fixture
66    }
67
68    /// Creates a `429 Too Many Requests` response.
69    #[must_use]
70    pub const fn rate_limited(body: FixtureBody<'a>, rate_limit: RateLimitFixture) -> Self {
71        let mut fixture = Self::new(FixtureKind::RateLimit, StatusCode::TOO_MANY_REQUESTS, body);
72        fixture.rate_limit = Some(rate_limit);
73        fixture
74    }
75
76    /// Adds rate-limit metadata to any response fixture.
77    #[must_use]
78    pub const fn with_rate_limit(mut self, rate_limit: RateLimitFixture) -> Self {
79        self.rate_limit = Some(rate_limit);
80        self
81    }
82
83    /// Adds one raw response content type for transport-boundary modeling.
84    #[must_use]
85    pub const fn with_content_type(mut self, content_type: &'a str) -> Self {
86        self.content_type = Some(content_type);
87        self
88    }
89
90    /// Creates a client or server error response.
91    pub const fn error(
92        status: StatusCode,
93        body: FixtureBody<'a>,
94    ) -> Result<Self, ResponseFixtureError> {
95        if !status.is_error() {
96            return Err(ResponseFixtureError::NonErrorStatus);
97        }
98        Ok(Self::new(FixtureKind::Error, status, body))
99    }
100
101    const fn new(kind: FixtureKind, status: StatusCode, body: FixtureBody<'a>) -> Self {
102        Self {
103            kind,
104            status,
105            body,
106            pagination: None,
107            action: None,
108            rate_limit: None,
109            content_type: None,
110        }
111    }
112
113    /// Returns the fixture category.
114    #[must_use]
115    pub const fn kind(self) -> FixtureKind {
116        self.kind
117    }
118
119    /// Returns the response status.
120    #[must_use]
121    pub const fn status(self) -> StatusCode {
122        self.status
123    }
124
125    /// Returns the response body source.
126    #[must_use]
127    pub const fn body(self) -> FixtureBody<'a> {
128        self.body
129    }
130
131    /// Returns pagination metadata when present.
132    #[must_use]
133    pub const fn pagination(self) -> Option<PaginationFixture> {
134        self.pagination
135    }
136
137    /// Returns action metadata when present.
138    #[must_use]
139    pub const fn action_metadata(self) -> Option<ActionFixture> {
140        self.action
141    }
142
143    /// Returns rate-limit metadata when present.
144    #[must_use]
145    pub const fn rate_limit(self) -> Option<RateLimitFixture> {
146        self.rate_limit
147    }
148
149    /// Returns the response content type when configured.
150    #[must_use]
151    pub const fn content_type(self) -> Option<&'a str> {
152        self.content_type
153    }
154}