Skip to content

Commit 15b8c06

Browse files
committed
fix: better behavioral test handling
Signed-off-by: Eren Atas <[email protected]>
1 parent cd758e0 commit 15b8c06

File tree

5 files changed

+53
-40
lines changed

5 files changed

+53
-40
lines changed

crates/flagd/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,9 @@ pub struct FlagdOptions {
261261
pub stream_deadline_ms: u32,
262262
/// Offline polling interval in milliseconds
263263
pub offline_poll_interval_ms: Option<u32>,
264+
/// Provider ID for identifying this provider instance to flagd
265+
/// Used in in-process resolver for sync requests
266+
pub provider_id: Option<String>,
264267
}
265268
/// Type of resolver to use for flag evaluation
266269
#[derive(Debug, Clone, PartialEq)]
@@ -336,6 +339,7 @@ impl Default for FlagdOptions {
336339
.and_then(|s| s.parse().ok())
337340
.unwrap_or(5000),
338341
),
342+
provider_id: std::env::var("FLAGD_PROVIDER_ID").ok(),
339343
};
340344

341345
if options.source_configuration.is_some() && options.resolver_type != ResolverType::Rpc {

crates/flagd/src/resolver/in_process/storage/connector/grpc.rs

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub struct GrpcStreamConnector {
2929
retry_grace_period: u32,
3030
stream_deadline_ms: u32,
3131
authority: String, // desired authority, e.g. "b-features-api.service"
32+
provider_id: String, // provider identifier for sync requests
3233
}
3334

3435
impl GrpcStreamConnector {
@@ -52,6 +53,7 @@ impl GrpcStreamConnector {
5253
retry_grace_period: options.retry_grace_period,
5354
stream_deadline_ms: options.stream_deadline_ms,
5455
authority,
56+
provider_id: options.provider_id.clone().unwrap_or_else(|| "rust-flagd-provider".to_string()),
5557
}
5658
}
5759

@@ -119,7 +121,7 @@ impl GrpcStreamConnector {
119121
// Create the gRPC client with no interceptor because the endpoint already carries the desired authority.
120122
let mut client = FlagSyncServiceClient::new(channel);
121123
let request = tonic::Request::new(SyncFlagsRequest {
122-
provider_id: "rust-flagd-provider".to_string(),
124+
provider_id: self.provider_id.clone(),
123125
selector: self.selector.clone().unwrap_or_default(),
124126
});
125127
debug!("Sending sync request with selector: {:?}", self.selector);
@@ -227,24 +229,17 @@ mod tests {
227229
drop(listener);
228230

229231
// Create options configured for a failing connection.
230-
let options = FlagdOptions {
231-
host: addr.ip().to_string(),
232-
resolver_type: crate::ResolverType::InProcess,
233-
port: addr.port(),
234-
target_uri: None,
235-
deadline_ms: 100, // Short timeout for fast failures
236-
retry_backoff_ms: 100,
237-
retry_backoff_max_ms: 400,
238-
retry_grace_period: 3,
239-
stream_deadline_ms: 500,
240-
tls: false,
241-
cert_path: None,
242-
selector: None,
243-
socket_path: None,
244-
cache_settings: None,
245-
source_configuration: None,
246-
offline_poll_interval_ms: None,
247-
};
232+
let mut options = FlagdOptions::default();
233+
options.host = addr.ip().to_string();
234+
options.resolver_type = crate::ResolverType::InProcess;
235+
options.port = addr.port();
236+
options.deadline_ms = 100; // Short timeout for fast failures
237+
options.retry_backoff_ms = 100;
238+
options.retry_backoff_max_ms = 400;
239+
options.retry_grace_period = 3;
240+
options.stream_deadline_ms = 500;
241+
options.tls = false;
242+
options.cache_settings = None;
248243

249244
let target = format!("{}:{}", addr.ip(), addr.port());
250245
let connector =

crates/flagd/tests/gherkin_config_test.rs

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,11 @@ async fn env_with_value(world: &mut ConfigWorld, env: String, value: String) {
106106

107107
#[when(expr = "a config was initialized")]
108108
async fn initialize_config(world: &mut ConfigWorld) {
109+
// Start with defaults (which reads from environment variables)
109110
let mut options = FlagdOptions::default();
111+
let mut resolver_explicitly_set = false;
110112

111-
// Handle resolver type first
113+
// Handle resolver type first - explicit options override env vars
112114
if let Some(resolver) = world.option_values.get("resolver") {
113115
options.resolver_type = match resolver.to_uppercase().as_str() {
114116
"RPC" => ResolverType::Rpc,
@@ -117,32 +119,27 @@ async fn initialize_config(world: &mut ConfigWorld) {
117119
"FILE" | "OFFLINE" => ResolverType::File,
118120
_ => ResolverType::Rpc,
119121
};
120-
} else if let Ok(resolver) = std::env::var("FLAGD_RESOLVER") {
121-
options.resolver_type = match resolver.to_uppercase().as_str() {
122-
"RPC" => ResolverType::Rpc,
123-
"REST" => ResolverType::Rest,
124-
"IN-PROCESS" | "INPROCESS" => ResolverType::InProcess,
125-
"FILE" | "OFFLINE" => ResolverType::File,
126-
_ => ResolverType::Rpc,
122+
resolver_explicitly_set = true;
123+
// Update port based on resolver type when explicitly set
124+
options.port = match options.resolver_type {
125+
ResolverType::Rpc => 8013,
126+
ResolverType::InProcess => 8015,
127+
_ => options.port,
127128
};
128129
}
129130

130-
// Set default port based on resolver type
131-
options.port = match options.resolver_type {
132-
ResolverType::Rpc => 8013,
133-
ResolverType::InProcess => 8015,
134-
_ => options.port,
135-
};
136-
137-
// Handle source configuration after resolver type
131+
// Handle source configuration - may override resolver type for backwards compatibility
132+
// BUT only if resolver wasn't explicitly set to "rpc"
138133
if let Some(source) = world.option_values.get("offlineFlagSourcePath") {
139134
options.source_configuration = Some(source.clone());
140-
if options.resolver_type != ResolverType::Rpc {
135+
// For backwards compatibility: if offline path is set, switch to File resolver
136+
// UNLESS resolver was explicitly set to "rpc" (in which case keep it as "rpc")
137+
if !resolver_explicitly_set || options.resolver_type != ResolverType::Rpc {
141138
options.resolver_type = ResolverType::File;
142139
}
143140
}
144141

145-
// Handle remaining explicit options
142+
// Handle remaining explicit options (these override env vars)
146143
if let Some(host) = world.option_values.get("host") {
147144
options.host = host.clone();
148145
}
@@ -217,6 +214,9 @@ async fn initialize_config(world: &mut ConfigWorld) {
217214
if let Some(selector) = world.option_values.get("selector") {
218215
options.selector = Some(selector.clone());
219216
}
217+
if let Some(provider_id) = world.option_values.get("providerId") {
218+
options.provider_id = Some(provider_id.clone());
219+
}
220220
if let Some(max_size) = world
221221
.option_values
222222
.get("maxCacheSize")
@@ -269,12 +269,26 @@ async fn check_option_value(
269269
"retryBackoffMaxMs" => Some(world.options.retry_backoff_max_ms.to_string()),
270270
"retryGracePeriod" => Some(world.options.retry_grace_period.to_string()),
271271
"selector" => world.options.selector.clone(),
272+
"providerId" => world.options.provider_id.clone(),
272273
"socketPath" => world.options.socket_path.clone(),
273274
"streamDeadlineMs" => Some(world.options.stream_deadline_ms.to_string()),
274275
_ => None,
275276
};
276277
let expected = convert_type(&option_type, &value);
277-
assert_eq!(actual, expected, "Option '{}' value mismatch", option);
278+
279+
// For resolver type, do case-insensitive comparison since enum normalizes to lowercase
280+
let actual_normalized = if option == "resolver" {
281+
actual.as_ref().map(|v| v.to_lowercase())
282+
} else {
283+
actual.clone()
284+
};
285+
let expected_normalized = if option == "resolver" {
286+
expected.as_ref().map(|v| v.to_lowercase())
287+
} else {
288+
expected.clone()
289+
};
290+
291+
assert_eq!(actual_normalized, expected_normalized, "Option '{}' value mismatch", option);
278292
}
279293

280294
#[test(tokio::test)]

0 commit comments

Comments
 (0)