faucet_source_webhook/
config.rs1use faucet_core::DEFAULT_BATCH_SIZE;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
9#[serde(default)]
10pub struct WebhookSourceConfig {
11 pub listen_addr: String,
18 pub path: String,
20 pub max_payloads: Option<usize>,
22 pub timeout_secs: u64,
24 pub max_body_bytes: usize,
28 pub auth_token: Option<String>,
33 #[serde(default = "default_batch_size")]
44 pub batch_size: usize,
45}
46
47fn default_batch_size() -> usize {
48 DEFAULT_BATCH_SIZE
49}
50
51impl Default for WebhookSourceConfig {
52 fn default() -> Self {
53 Self {
54 listen_addr: "127.0.0.1:8080".into(),
55 path: "/webhook".into(),
56 max_payloads: None,
57 timeout_secs: 30,
58 max_body_bytes: 1024 * 1024,
59 auth_token: None,
60 batch_size: DEFAULT_BATCH_SIZE,
61 }
62 }
63}
64
65impl WebhookSourceConfig {
66 pub fn new() -> Self {
68 Self::default()
69 }
70
71 pub fn listen_addr(mut self, addr: impl Into<String>) -> Self {
73 self.listen_addr = addr.into();
74 self
75 }
76
77 pub fn path(mut self, path: impl Into<String>) -> Self {
79 self.path = path.into();
80 self
81 }
82
83 pub fn max_payloads(mut self, max: usize) -> Self {
85 self.max_payloads = Some(max);
86 self
87 }
88
89 pub fn timeout_secs(mut self, secs: u64) -> Self {
91 self.timeout_secs = secs;
92 self
93 }
94
95 pub fn max_body_bytes(mut self, bytes: usize) -> Self {
97 self.max_body_bytes = bytes;
98 self
99 }
100
101 pub fn auth_token(mut self, token: impl Into<String>) -> Self {
103 self.auth_token = Some(token.into());
104 self
105 }
106
107 pub fn with_batch_size(mut self, batch_size: usize) -> Self {
115 self.batch_size = batch_size;
116 self
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn default_config() {
126 let config = WebhookSourceConfig::new();
127 assert_eq!(
128 config.listen_addr, "127.0.0.1:8080",
129 "default must bind loopback only, not 0.0.0.0"
130 );
131 assert_eq!(config.path, "/webhook");
132 assert!(config.max_payloads.is_none());
133 assert_eq!(config.timeout_secs, 30);
134 assert_eq!(config.max_body_bytes, 1024 * 1024);
135 assert!(config.auth_token.is_none());
136 }
137
138 #[test]
139 fn builder_methods() {
140 let config = WebhookSourceConfig::new()
141 .listen_addr("127.0.0.1:9090")
142 .path("/hooks/incoming")
143 .max_payloads(10)
144 .timeout_secs(60);
145 assert_eq!(config.listen_addr, "127.0.0.1:9090");
146 assert_eq!(config.path, "/hooks/incoming");
147 assert_eq!(config.max_payloads, Some(10));
148 assert_eq!(config.timeout_secs, 60);
149 }
150
151 #[test]
152 fn batch_size_defaults_to_default_batch_size() {
153 let config = WebhookSourceConfig::new();
154 assert_eq!(config.batch_size, faucet_core::DEFAULT_BATCH_SIZE);
155 }
156
157 #[test]
158 fn with_batch_size_overrides_default() {
159 let config = WebhookSourceConfig::new().with_batch_size(250);
160 assert_eq!(config.batch_size, 250);
161 }
162
163 #[test]
164 fn batch_size_zero_is_accepted_as_no_batching_sentinel() {
165 let config = WebhookSourceConfig::new().with_batch_size(0);
166 assert_eq!(config.batch_size, 0);
167 assert!(faucet_core::validate_batch_size(config.batch_size).is_ok());
168 }
169
170 #[test]
171 fn batch_size_above_max_is_rejected_by_validate_batch_size() {
172 let config = WebhookSourceConfig::new().with_batch_size(faucet_core::MAX_BATCH_SIZE + 1);
173 assert!(faucet_core::validate_batch_size(config.batch_size).is_err());
174 }
175
176 #[test]
177 fn batch_size_deserializes_from_json() {
178 let json = r#"{
179 "listen_addr": "127.0.0.1:8080",
180 "path": "/webhook",
181 "timeout_secs": 30,
182 "batch_size": 500
183 }"#;
184 let config: WebhookSourceConfig = serde_json::from_str(json).unwrap();
185 assert_eq!(config.batch_size, 500);
186 }
187}