use vynfi::{
Client, CreateApiKeyRequest, GenerateRequest, ListJobsParams, TableSpec, UpdateApiKeyRequest,
VynFiError,
};
fn client() -> Client {
let api_key = std::env::var("VYNFI_API_KEY").expect("VYNFI_API_KEY must be set");
let mut builder = Client::builder(api_key);
if let Ok(url) = std::env::var("VYNFI_BASE_URL") {
builder = builder.base_url(url);
}
builder.build().expect("failed to build client")
}
#[tokio::test]
#[ignore]
async fn catalog_list_sectors() {
let c = client();
let sectors = c.catalog().list_sectors().await.unwrap();
assert!(!sectors.is_empty(), "expected at least one sector");
for s in §ors {
assert!(!s.slug.is_empty());
assert!(s.table_count > 0, "sector {} has 0 tables", s.slug);
}
}
#[tokio::test]
#[ignore]
async fn catalog_get_sector_retail() {
let c = client();
let sector = c.catalog().get_sector("retail").await.unwrap();
assert_eq!(sector.slug, "retail");
assert!(!sector.tables.is_empty(), "retail should have tables");
let names: Vec<&str> = sector.tables.iter().map(|t| t.name.as_str()).collect();
assert!(
names.contains(&"journal_entries"),
"retail should include journal_entries, got: {names:?}"
);
}
#[tokio::test]
#[ignore]
async fn catalog_get_sector_not_found() {
let c = client();
let err = c
.catalog()
.get_sector("nonexistent-sector-slug")
.await
.unwrap_err();
assert!(
matches!(err, VynFiError::NotFound(_)),
"expected NotFound, got: {err:?}"
);
}
#[tokio::test]
#[ignore]
async fn usage_summary() {
let c = client();
let summary = c.usage().summary().await.unwrap();
assert!(summary.period_days > 0);
assert!(!summary.tier.is_empty());
}
#[tokio::test]
#[ignore]
async fn usage_daily() {
let c = client();
let resp = c.usage().daily(Some(7)).await.unwrap();
assert!(resp.daily.len() <= 7);
}
#[tokio::test]
#[ignore]
async fn jobs_list() {
let c = client();
let list = c
.jobs()
.list(&ListJobsParams {
limit: Some(5),
..Default::default()
})
.await
.unwrap();
for job in &list.jobs {
assert!(!job.id.is_empty());
assert!(!job.status.is_empty());
}
}
#[tokio::test]
#[ignore]
async fn jobs_get_not_found() {
let c = client();
let err = c
.jobs()
.get("00000000-0000-0000-0000-000000000000")
.await
.unwrap_err();
assert!(
matches!(err, VynFiError::NotFound(_)),
"expected NotFound, got: {err:?}"
);
}
#[tokio::test]
#[ignore]
async fn jobs_generate_quick_and_download() {
let c = client();
let req = GenerateRequest::new(
vec![TableSpec {
name: "journal_entries".to_string(),
rows: 10,
}],
"retail",
);
let job = c.jobs().generate_quick(&req).await.unwrap();
assert_eq!(job.status, "completed");
assert!(job.rows_generated.unwrap_or(0) > 0);
assert!(job.credits_used.unwrap_or(0) > 0);
let fetched = c.jobs().get(&job.id).await.unwrap();
assert_eq!(fetched.id, job.id);
assert_eq!(fetched.status, "completed");
let dl = c.jobs().download(&job.id).await.unwrap();
assert!(!dl.url.is_empty());
assert!(dl.expires_in > 0);
}
#[tokio::test]
#[ignore]
async fn jobs_generate_async() {
let c = client();
let req = GenerateRequest::new(
vec![TableSpec {
name: "journal_entries".to_string(),
rows: 10,
}],
"retail",
);
let resp = c.jobs().generate(&req).await.unwrap();
assert!(!resp.id.is_empty());
assert!(!resp.status.is_empty());
assert!(resp.credits_reserved > 0);
}
#[tokio::test]
#[ignore]
async fn jobs_generate_validation_error() {
let c = client();
let req = GenerateRequest {
tables: vec![],
format: None,
sector_slug: "retail".to_string(),
};
let err = c.jobs().generate_quick(&req).await.unwrap_err();
assert!(
matches!(err, VynFiError::Validation(_)),
"expected Validation, got: {err:?}"
);
}
#[tokio::test]
#[ignore]
async fn api_keys_lifecycle() {
let c = client();
let created = c
.api_keys()
.create(&CreateApiKeyRequest {
name: "integration-test-key".to_string(),
scopes: Some(vec!["catalog:read".to_string()]),
environment: Some("test".to_string()),
expires_in_days: Some(1),
})
.await
.unwrap();
assert!(!created.id.is_empty());
assert!(!created.key.is_empty(), "full secret should be returned");
assert!(
created.key.starts_with("vf_test_"),
"test env key should have vf_test_ prefix, got: {}",
created.key
);
assert_eq!(created.name, "integration-test-key");
assert_eq!(created.environment, "test");
let key_id = created.id.clone();
let keys = c.api_keys().list().await.unwrap();
assert!(
keys.iter().any(|k| k.id == key_id),
"newly created key should appear in list"
);
let fetched = c.api_keys().get(&key_id).await.unwrap();
assert_eq!(fetched.id, key_id);
assert_eq!(fetched.name, "integration-test-key");
let updated = c
.api_keys()
.update(
&key_id,
&UpdateApiKeyRequest {
name: Some("integration-test-key-updated".to_string()),
scopes: None,
},
)
.await
.unwrap();
assert_eq!(updated.name, "integration-test-key-updated");
c.api_keys().revoke(&key_id).await.unwrap();
let keys_after = c.api_keys().list().await.unwrap();
let revoked = keys_after.iter().find(|k| k.id == key_id);
if let Some(k) = revoked {
assert_eq!(k.status, "revoked");
}
}
#[tokio::test]
#[ignore]
async fn credits_balance() {
let c = client();
let balance = c.credits().balance().await.unwrap();
assert!(balance.total_prepaid_credits >= 0);
}
#[tokio::test]
#[ignore]
async fn credits_history() {
let c = client();
let history = c.credits().history().await.unwrap();
for batch in &history.batches {
assert!(!batch.batch_id.is_empty());
assert!(!batch.pack.is_empty());
}
}