Skip to content

Commit c2aae7b

Browse files
committed
test: add fixture-based validation tests for Enterprise API
- Created generate-enterprise-fixtures.sh script to capture real API responses - Generated 21 fixtures from live Docker Enterprise cluster - Added fixture_validation_tests.rs with 8 tests validating type definitions - All tests pass except 1 intentionally ignored (found type bug) Findings: - Discovered Module struct type mismatch: bigstore_version_2_support is bool in API but defined as map in struct - Stats endpoints return arrays not objects - All other core types (Cluster, Database, Node, User, License) deserialize correctly This fixture-based approach will catch type mismatches that manual mocks miss, addressing issues #347, #348, #351. Related to #352
1 parent 6a78a0b commit c2aae7b

22 files changed

+212
-711
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//! Fixture-based validation tests
2+
//!
3+
//! These tests use real API responses captured from a Redis Enterprise cluster
4+
//! to validate that our Rust type definitions accurately match the actual API.
5+
//!
6+
//! ## Known Issues Found
7+
//!
8+
//! - Module struct has type mismatch: bigstore_version_2_support should be bool, not map
9+
10+
use redis_enterprise::{ClusterInfo, Database, License, Node, User};
11+
use serde_json::Value;
12+
13+
#[test]
14+
fn test_cluster_info_from_fixture() {
15+
let fixture = include_str!("fixtures/cluster.json");
16+
let cluster: ClusterInfo =
17+
serde_json::from_str(fixture).expect("Failed to deserialize cluster info");
18+
assert!(!cluster.name.is_empty());
19+
}
20+
21+
#[test]
22+
fn test_database_list_from_fixture() {
23+
let fixture = include_str!("fixtures/bdbs_list.json");
24+
let databases: Vec<Database> =
25+
serde_json::from_str(fixture).expect("Failed to deserialize database list");
26+
assert!(!databases.is_empty());
27+
}
28+
29+
#[test]
30+
fn test_single_database_from_fixture() {
31+
let fixture = include_str!("fixtures/bdb_single.json");
32+
let _database: Database =
33+
serde_json::from_str(fixture).expect("Failed to deserialize single database");
34+
}
35+
36+
#[test]
37+
fn test_nodes_list_from_fixture() {
38+
let fixture = include_str!("fixtures/nodes_list.json");
39+
let nodes: Vec<Node> = serde_json::from_str(fixture).expect("Failed to deserialize nodes list");
40+
assert!(!nodes.is_empty());
41+
}
42+
43+
#[test]
44+
fn test_users_list_from_fixture() {
45+
let fixture = include_str!("fixtures/users_list.json");
46+
let users: Vec<User> = serde_json::from_str(fixture).expect("Failed to deserialize users list");
47+
assert!(!users.is_empty());
48+
}
49+
50+
#[test]
51+
#[ignore = "Known type mismatch - bigstore_version_2_support should be bool not map"]
52+
fn test_modules_list_from_fixture() {
53+
let fixture = include_str!("fixtures/modules_list.json");
54+
// This will fail until Module struct is fixed
55+
let _modules: Vec<serde_json::Value> =
56+
serde_json::from_str(fixture).expect("Failed to deserialize modules list");
57+
}
58+
59+
#[test]
60+
fn test_license_from_fixture() {
61+
let fixture = include_str!("fixtures/license.json");
62+
let _license: License = serde_json::from_str(fixture).expect("Failed to deserialize license");
63+
}
64+
65+
#[test]
66+
fn test_stats_from_fixtures() {
67+
// Test cluster stats
68+
let cluster_stats: Value = serde_json::from_str(include_str!("fixtures/cluster_stats.json"))
69+
.expect("Failed to deserialize cluster stats");
70+
assert!(cluster_stats.is_object());
71+
72+
// Test database stats - these are arrays not objects
73+
let db_stats: Value = serde_json::from_str(include_str!("fixtures/bdbs_stats.json"))
74+
.expect("Failed to deserialize database stats");
75+
assert!(db_stats.is_array());
76+
77+
// Test node stats - these are arrays not objects
78+
let node_stats: Value = serde_json::from_str(include_str!("fixtures/nodes_stats.json"))
79+
.expect("Failed to deserialize node stats");
80+
assert!(node_stats.is_array());
81+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"actions":[{"action_uid":"8fc32a11-4688-4057-9237-cd77fccd726f","creation_time":"1760400087","name":"retry_bdb","node_uid":"1","progress":"100","status":"completed","task_id":"8fc32a11-4688-4057-9237-cd77fccd726f"}],"state-machines":[{"action_uid":"04d7b6ea-f377-4d40-9b23-9a8f291988df","heartbeat":1760400082,"name":"SMCreateBDB","object_name":"bdb:1","status":"completed"}]}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"bdb_groups":[]}
Lines changed: 1 addition & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -1,185 +1 @@
1-
{
2-
"acl": [],
3-
"active_defrag_cycle_max": 25,
4-
"active_defrag_cycle_min": 1,
5-
"active_defrag_ignore_bytes": "104857600",
6-
"active_defrag_max_scan_fields": 1000,
7-
"active_defrag_threshold_lower": 10,
8-
"active_defrag_threshold_upper": 100,
9-
"activedefrag": "no",
10-
"aof_policy": "appendfsync-every-sec",
11-
"authentication_admin_pass": "6KioqIaX0iEU4BbQ5sMjE471Q5wljEuVz4nwFts3x34KDkbT",
12-
"authentication_redis_pass": "",
13-
"authentication_sasl_pass": "",
14-
"authentication_sasl_uname": "",
15-
"authentication_ssl_client_certs": [],
16-
"authentication_ssl_crdt_certs": [],
17-
"authorized_subjects": [],
18-
"auto_upgrade": false,
19-
"background_op": [
20-
{
21-
"status": "idle"
22-
}
23-
],
24-
"backup": false,
25-
"backup_failure_reason": "",
26-
"backup_history": 0,
27-
"backup_interval": 86400,
28-
"backup_progress": 0.0,
29-
"backup_status": "",
30-
"bigstore": false,
31-
"bigstore_max_ram_ratio": 10,
32-
"bigstore_ram_size": 0,
33-
"bigstore_version": 1,
34-
"client_cert_subject_validation_type": "disabled",
35-
"compare_key_hslot": false,
36-
"conns": 5,
37-
"conns_type": "per-thread",
38-
"crdt": false,
39-
"crdt_causal_consistency": false,
40-
"crdt_config_version": 0,
41-
"crdt_ghost_replica_ids": "",
42-
"crdt_guid": "",
43-
"crdt_modules": "[]",
44-
"crdt_repl_backlog_size": "auto",
45-
"crdt_replica_id": 0,
46-
"crdt_replicas": "",
47-
"crdt_sources": [],
48-
"crdt_sync": "disabled",
49-
"crdt_sync_connection_alarm_timeout_seconds": 0,
50-
"crdt_sync_dist": true,
51-
"crdt_syncer_auto_oom_unlatch": true,
52-
"crdt_xadd_id_uniqueness_mode": "strict",
53-
"created_time": "2025-09-16T21:34:19Z",
54-
"data_internode_encryption": false,
55-
"data_persistence": "disabled",
56-
"dataset_import_sources": [],
57-
"db_conns_auditing": false,
58-
"default_user": true,
59-
"dns_address_master": "",
60-
"dns_suffixes": [],
61-
"email_alerts": false,
62-
"endpoints": [
63-
{
64-
"addr": [
65-
"192.168.32.2"
66-
],
67-
"addr_type": "external",
68-
"dns_name": "redis-11657.docker-cluster",
69-
"oss_cluster_api_preferred_endpoint_type": "ip",
70-
"oss_cluster_api_preferred_ip_type": "internal",
71-
"port": 11657,
72-
"proxy_policy": "single",
73-
"uid": "2:1"
74-
}
75-
],
76-
"eviction_policy": "volatile-lru",
77-
"export_failure_reason": "",
78-
"export_progress": 0.0,
79-
"export_status": "",
80-
"flush_on_fullsync": true,
81-
"generate_text_monitor": false,
82-
"gradual_src_max_sources": 1,
83-
"gradual_src_mode": "disabled",
84-
"gradual_sync_max_shards_per_source": 1,
85-
"gradual_sync_mode": "auto",
86-
"group_uid": 0,
87-
"hash_slots_policy": "16k",
88-
"implicit_shard_key": false,
89-
"import_failure_reason": "",
90-
"import_progress": 0.0,
91-
"import_status": "",
92-
"internal": false,
93-
"last_changed_time": "2025-09-16T21:34:19Z",
94-
"master_persistence": false,
95-
"max_aof_file_size": 322122547200,
96-
"max_aof_load_time": 3600,
97-
"max_client_pipeline": 200,
98-
"max_connections": 0,
99-
"max_pipelined": 2000,
100-
"maxclients": 10000,
101-
"memory_size": 107374182,
102-
"metrics_export_all": false,
103-
"mkms": true,
104-
"module_list": [
105-
{
106-
"module_args": "",
107-
"module_id": "4e6efe7fa848e35cbae142dd1e4564a2",
108-
"module_name": "search",
109-
"semantic_version": "7.99.91"
110-
},
111-
{
112-
"module_args": "",
113-
"module_id": "b14385b09778a335835ee3ae380bbc77",
114-
"module_name": "bf",
115-
"semantic_version": "8.0.1"
116-
},
117-
{
118-
"module_args": "",
119-
"module_id": "259bc961ae0ed766d6294bcdb7af9039",
120-
"module_name": "ReJSON",
121-
"semantic_version": "7.99.3"
122-
},
123-
{
124-
"module_args": "",
125-
"module_id": "891142de155df09c6739543b422178f4",
126-
"module_name": "timeseries",
127-
"semantic_version": "8.0.2"
128-
}
129-
],
130-
"mtls_allow_outdated_certs": false,
131-
"mtls_allow_weak_hashing": false,
132-
"multi_commands_opt": "auto",
133-
"name": "test-db",
134-
"oss_cluster": false,
135-
"oss_cluster_api_preferred_endpoint_type": "ip",
136-
"oss_cluster_api_preferred_ip_type": "internal",
137-
"oss_sharding": false,
138-
"port": 0,
139-
"proxy_policy": "single",
140-
"rack_aware": false,
141-
"recovery_wait_time": -1,
142-
"redis_cluster_enabled": false,
143-
"redis_version": "8.0",
144-
"repl_backlog_size": "auto",
145-
"replica_read_only": false,
146-
"replica_sources": [],
147-
"replica_sync": "disabled",
148-
"replica_sync_connection_alarm_timeout_seconds": 0,
149-
"replica_sync_dist": true,
150-
"replication": false,
151-
"resp3": true,
152-
"roles_permissions": [],
153-
"sched_policy": "cmp",
154-
"shard_block_crossslot_keys": false,
155-
"shard_block_foreign_keys": true,
156-
"shard_key_regex": [],
157-
"shard_list": [
158-
1
159-
],
160-
"sharding": false,
161-
"shards_count": 1,
162-
"shards_placement": "dense",
163-
"skip_import_analyze": "disabled",
164-
"slave_buffer": "auto",
165-
"slave_ha": false,
166-
"slave_ha_priority": 0,
167-
"snapshot_policy": [],
168-
"ssl": false,
169-
"status": "active",
170-
"support_syncer_reconf": true,
171-
"sync": "disabled",
172-
"sync_dedicated_threads": 5,
173-
"sync_sources": [],
174-
"syncer_log_level": "info",
175-
"syncer_mode": "distributed",
176-
"throughput_ingress": 0,
177-
"tls_mode": "disabled",
178-
"topology_epoch": 1,
179-
"tracking_table_max_keys": 1000000,
180-
"type": "redis",
181-
"uid": 2,
182-
"use_selective_flush": true,
183-
"version": "8.0.0",
184-
"wait_command": true
185-
}
1+
{"acl":[],"active_defrag_cycle_max":25,"active_defrag_cycle_min":1,"active_defrag_ignore_bytes":"104857600","active_defrag_max_scan_fields":1000,"active_defrag_threshold_lower":10,"active_defrag_threshold_upper":100,"activedefrag":"no","aof_policy":"appendfsync-every-sec","authentication_admin_pass":"MTRp3pu6VGSojFkfw5v1iiHFnU3NYFvUHtoMDybsZAufxDfL","authentication_redis_pass":"","authentication_sasl_pass":"","authentication_sasl_uname":"","authentication_ssl_client_certs":[],"authentication_ssl_crdt_certs":[],"authorized_subjects":[],"auto_upgrade":false,"background_op":[{"status":"idle"}],"backup":false,"backup_failure_reason":"","backup_history":0,"backup_interval":86400,"backup_progress":0.0,"backup_status":"","bigstore":false,"bigstore_max_ram_ratio":10,"bigstore_ram_size":0,"bigstore_version":1,"client_cert_subject_validation_type":"disabled","compare_key_hslot":false,"conns":5,"conns_type":"per-thread","crdt":false,"crdt_causal_consistency":false,"crdt_config_version":0,"crdt_ghost_replica_ids":"","crdt_guid":"","crdt_modules":"[]","crdt_repl_backlog_size":"auto","crdt_replica_id":0,"crdt_replicas":"","crdt_sources":[],"crdt_sync":"disabled","crdt_sync_connection_alarm_timeout_seconds":0,"crdt_sync_dist":true,"crdt_syncer_auto_oom_unlatch":true,"crdt_xadd_id_uniqueness_mode":"strict","created_time":"2025-10-14T00:01:22Z","data_internode_encryption":false,"data_persistence":"disabled","dataset_import_sources":[],"db_conns_auditing":false,"default_user":true,"dns_address_master":"","dns_suffixes":[],"email_alerts":false,"endpoints":[{"addr":["192.168.16.2"],"addr_type":"external","dns_name":"redis-14968.docker-cluster","oss_cluster_api_preferred_endpoint_type":"ip","oss_cluster_api_preferred_ip_type":"internal","port":14968,"proxy_policy":"single","uid":"1:1"}],"eviction_policy":"volatile-lru","export_failure_reason":"","export_progress":0.0,"export_status":"","flush_on_fullsync":true,"generate_text_monitor":false,"gradual_src_max_sources":1,"gradual_src_mode":"disabled","gradual_sync_max_shards_per_source":1,"gradual_sync_mode":"auto","group_uid":0,"hash_slots_policy":"16k","implicit_shard_key":false,"import_failure_reason":"","import_progress":0.0,"import_status":"","internal":false,"last_changed_time":"2025-10-14T00:01:22Z","master_persistence":false,"max_aof_file_size":322122547200,"max_aof_load_time":3600,"max_client_pipeline":200,"max_connections":0,"max_pipelined":2000,"maxclients":10000,"memory_size":1073741824,"metrics_export_all":false,"mkms":true,"module_list":[{"module_args":"","module_id":"891142de155df09c6739543b422178f4","module_name":"timeseries","semantic_version":"8.0.2"},{"module_args":"","module_id":"4e6efe7fa848e35cbae142dd1e4564a2","module_name":"search","semantic_version":"7.99.91"},{"module_args":"","module_id":"b14385b09778a335835ee3ae380bbc77","module_name":"bf","semantic_version":"8.0.1"},{"module_args":"","module_id":"259bc961ae0ed766d6294bcdb7af9039","module_name":"ReJSON","semantic_version":"7.99.3"}],"mtls_allow_outdated_certs":false,"mtls_allow_weak_hashing":false,"multi_commands_opt":"auto","name":"default-db","oss_cluster":false,"oss_cluster_api_preferred_endpoint_type":"ip","oss_cluster_api_preferred_ip_type":"internal","oss_sharding":false,"port":0,"proxy_policy":"single","rack_aware":false,"recovery_wait_time":-1,"redis_cluster_enabled":false,"redis_version":"8.0","repl_backlog_size":"auto","replica_read_only":false,"replica_sources":[],"replica_sync":"disabled","replica_sync_connection_alarm_timeout_seconds":0,"replica_sync_dist":true,"replication":false,"resp3":true,"roles_permissions":[],"sched_policy":"cmp","shard_block_crossslot_keys":false,"shard_block_foreign_keys":true,"shard_key_regex":[],"shard_list":[1],"sharding":false,"shards_count":1,"shards_placement":"dense","skip_import_analyze":"disabled","slave_buffer":"auto","slave_ha":false,"slave_ha_priority":0,"snapshot_policy":[],"ssl":false,"status":"active","support_syncer_reconf":true,"sync":"disabled","sync_dedicated_threads":5,"sync_sources":[],"syncer_log_level":"info","syncer_mode":"distributed","throughput_ingress":0,"tls_mode":"disabled","topology_epoch":1,"tracking_table_max_keys":1000000,"type":"redis","uid":1,"use_selective_flush":true,"version":"8.0.0","wait_command":true}

0 commit comments

Comments
 (0)