use atproto_identity::key::{KeyType, generate_key}; use atproto_oauth::workflow::OAuthClient; use axum::Json; use axum::routing::get; use serde_json::json; use zkwid_dev::{Devnet, N_HOST}; #[tokio::test(flavor = "multi_thread")] async fn devnet() -> anyhow::Result<()> { let devnet = Devnet::start().await?; assert!(devnet.user.did.starts_with("did:plc:")); let bytes = b"zkwid devnet blob".to_vec(); let blob = devnet.upload_blob(&bytes, "application/octet-stream").await?; assert_eq!(blob.link.link, atproto_dasl::compute_raw_cid(&bytes).to_string()); assert_eq!(blob.size, bytes.len() as u64); let collection = "at.zkwid.demo.log"; let record = json!({ "$type": collection, "text": "hello", "createdAt": "2026-11-05T00:00:00.000Z" }); let strong = devnet.put_record(collection, "one", &record).await?; assert_eq!(strong.uri, format!("at://{}/{collection}/one", devnet.user.did)); let (value, cid) = devnet.get_record(collection, "one").await?.expect("record"); assert_eq!(value["text"], "hello"); assert_eq!(cid, strong.cid); let listed = devnet.list_records(collection).await?; assert_eq!(listed.len(), 1); assert_eq!(listed[0].0, strong.uri); devnet.delete_record(collection, "one").await?; assert!(devnet.get_record(collection, "one").await?.is_none()); assert!(devnet.list_records(collection).await?.is_empty()); let client_key = generate_key(KeyType::P256Private)?; let client_id = format!("https://{N_HOST}/oauth-client-metadata.json"); let redirect_uri = format!("https://{N_HOST}/cb"); let scope = "atproto repo:at.zkwid.demo.log"; let metadata = json!({ "client_id": client_id, "client_name": "zkwid devnet test", "client_uri": format!("https://{N_HOST}"), "redirect_uris": [redirect_uri], "scope": scope, "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], "token_endpoint_auth_method": "private_key_jwt", "token_endpoint_auth_signing_alg": "ES256", "application_type": "web", "dpop_bound_access_tokens": true, "jwks": { "keys": [atproto_oauth::jwk::generate(&client_key)?] }, }); let app = axum::Router::new() .route("/x", get(|| async { "x marks the spot" })) .route("/oauth-client-metadata.json", get(move || async move { Json(metadata) })); devnet.serve(N_HOST, app).await?; let response = devnet.http().get(format!("https://{N_HOST}/x")).send().await?; assert_eq!(response.status(), 200); assert_eq!(response.text().await?, "x marks the spot"); let unknown = devnet.http().get("https://unknown.zkwid.test/x").send().await; assert!(unknown.is_err()); let client = OAuthClient { redirect_uri: redirect_uri.clone(), client_id, private_signing_key_data: client_key }; let login = devnet.oauth_login(&client, scope).await?; assert!(login.redirect.starts_with(&redirect_uri), "{}", login.redirect); assert!(login.redirect.contains("code="), "{}", login.redirect); assert_eq!(login.tokens.sub.as_deref(), Some(devnet.user.did.as_str())); assert!(login.tokens.scope.contains("repo:at.zkwid.demo.log"), "{}", login.tokens.scope); Ok(()) }