Skip to main content

cloud_sdk_testkit/
response.rs

1//! Deterministic response fixture builders.
2
3use cloud_sdk::transport::{ResponseHeaders, 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)]
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    headers: ResponseHeaders,
44}
45
46impl<'a> ResponseFixture<'a> {
47    /// Creates a `200 OK` response.
48    #[must_use]
49    pub const fn success(body: FixtureBody<'a>) -> Self {
50        Self::new(FixtureKind::Success, StatusCode::OK, body)
51    }
52
53    /// Creates a `200 OK` paginated response.
54    #[must_use]
55    pub const fn paginated(body: FixtureBody<'a>, pagination: PaginationFixture) -> Self {
56        let mut fixture = Self::new(FixtureKind::Pagination, StatusCode::OK, body);
57        fixture.pagination = Some(pagination);
58        fixture
59    }
60
61    /// Creates a `200 OK` action response.
62    #[must_use]
63    pub const fn action(body: FixtureBody<'a>, action: ActionFixture) -> Self {
64        let mut fixture = Self::new(FixtureKind::Action, StatusCode::OK, body);
65        fixture.action = Some(action);
66        fixture
67    }
68
69    /// Creates a `429 Too Many Requests` response.
70    #[must_use]
71    pub const fn rate_limited(body: FixtureBody<'a>, rate_limit: RateLimitFixture) -> Self {
72        let mut fixture = Self::new(FixtureKind::RateLimit, StatusCode::TOO_MANY_REQUESTS, body);
73        fixture.rate_limit = Some(rate_limit);
74        fixture
75    }
76
77    /// Adds rate-limit metadata to any response fixture.
78    #[must_use]
79    pub const fn with_rate_limit(mut self, rate_limit: RateLimitFixture) -> Self {
80        self.rate_limit = Some(rate_limit);
81        self
82    }
83
84    /// Adds one raw response content type for transport-boundary modeling.
85    #[must_use]
86    pub const fn with_content_type(mut self, content_type: &'a str) -> Self {
87        self.content_type = Some(content_type);
88        self
89    }
90
91    /// Adds complete prevalidated response-header metadata.
92    #[must_use]
93    pub const fn with_headers(mut self, headers: ResponseHeaders) -> Self {
94        self.headers = headers;
95        self
96    }
97
98    /// Creates a client or server error response.
99    pub const fn error(
100        status: StatusCode,
101        body: FixtureBody<'a>,
102    ) -> Result<Self, ResponseFixtureError> {
103        if !status.is_error() {
104            return Err(ResponseFixtureError::NonErrorStatus);
105        }
106        Ok(Self::new(FixtureKind::Error, status, body))
107    }
108
109    const fn new(kind: FixtureKind, status: StatusCode, body: FixtureBody<'a>) -> Self {
110        Self {
111            kind,
112            status,
113            body,
114            pagination: None,
115            action: None,
116            rate_limit: None,
117            content_type: None,
118            headers: ResponseHeaders::new(),
119        }
120    }
121
122    /// Returns the fixture category.
123    #[must_use]
124    pub const fn kind(self) -> FixtureKind {
125        self.kind
126    }
127
128    /// Returns the response status.
129    #[must_use]
130    pub const fn status(self) -> StatusCode {
131        self.status
132    }
133
134    /// Returns the response body source.
135    #[must_use]
136    pub const fn body(self) -> FixtureBody<'a> {
137        self.body
138    }
139
140    /// Returns pagination metadata when present.
141    #[must_use]
142    pub const fn pagination(self) -> Option<PaginationFixture> {
143        self.pagination
144    }
145
146    /// Returns action metadata when present.
147    #[must_use]
148    pub const fn action_metadata(self) -> Option<ActionFixture> {
149        self.action
150    }
151
152    /// Returns rate-limit metadata when present.
153    #[must_use]
154    pub const fn rate_limit(self) -> Option<RateLimitFixture> {
155        self.rate_limit
156    }
157
158    /// Returns the response content type when configured.
159    #[must_use]
160    pub const fn content_type(self) -> Option<&'a str> {
161        self.content_type
162    }
163
164    /// Returns complete prevalidated response-header metadata.
165    #[must_use]
166    pub const fn headers(self) -> ResponseHeaders {
167        self.headers
168    }
169}