use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebvhServerRecord {
pub id: String,
pub did: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub access_token: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub access_expires_at: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub refresh_token: Option<String>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebvhDidRecord {
pub did: String,
pub server_id: String,
pub mnemonic: String,
pub scid: String,
pub context_id: String,
pub portable: bool,
pub log_entry_count: u32,
#[serde(default)]
pub pre_rotation_count: u32,
#[serde(default = "default_next_fragment_id")]
pub next_fragment_id: u32,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
fn default_next_fragment_id() -> u32 {
1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn legacy_record_loads_with_default_pre_rotation_and_fragment_id() {
let json = r#"{
"did": "did:webvh:Q...:vta.example.com:primary",
"server_id": "serverless",
"mnemonic": "",
"scid": "Q...",
"context_id": "primary",
"portable": false,
"log_entry_count": 1,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}"#;
let r: WebvhDidRecord = serde_json::from_str(json).unwrap();
assert_eq!(r.pre_rotation_count, 0);
assert_eq!(r.next_fragment_id, 1);
}
#[test]
fn record_with_new_fields_round_trips() {
let r = WebvhDidRecord {
did: "did:webvh:abc:vta.example.com:primary".into(),
server_id: "serverless".into(),
mnemonic: "".into(),
scid: "abc".into(),
context_id: "primary".into(),
portable: false,
log_entry_count: 3,
pre_rotation_count: 2,
next_fragment_id: 5,
created_at: Utc::now(),
updated_at: Utc::now(),
};
let json = serde_json::to_string(&r).unwrap();
let restored: WebvhDidRecord = serde_json::from_str(&json).unwrap();
assert_eq!(restored.pre_rotation_count, 2);
assert_eq!(restored.next_fragment_id, 5);
assert_eq!(restored.log_entry_count, 3);
}
}