perlsky is a Perl 5 implementation of an AT Protocol Personal Data Server.
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

Split blob sync happy paths from extended API suite

alice 7e1ce7f4 18027938

+97 -45
+5 -4
docs/TEST_AUDIT.md
··· 13 13 The current baseline for saying "the audited suite is green" is: 14 14 15 15 - `prove -lr t` 16 - - latest full green result in the realigned Meridian worktree: `Files=55, Tests=3047` 16 + - latest full green result in the realigned Meridian worktree: `Files=56, Tests=3049` 17 17 - `prove -lv t/server-auth.t` 18 18 - `perl -c script/differential-validate` 19 19 - `PERLSKY_RUN_REFERENCE_DIFF=1 prove -lv t/reference-differential.t` ··· 116 116 Current suite counts by bucket: 117 117 118 118 - `direct reference differential`: `5` 119 - - `audited local regression`: `37` 119 + - `audited local regression`: `38` 120 120 - `local correctness/infrastructure`: `13` 121 121 122 122 | Test file | Bucket | Current note | ··· 128 128 | `t/admin-account-surfaces.t` | audited local regression | isolated admin account-maintenance coverage for handle/email/password/signing-key/send-email/subject-status behaviors | 129 129 | `t/account-migration-auth.t` | audited local regression | explicit-`did` account creation requires authenticated migration service-auth and preserves remote DID-doc state while starting deactivated | 130 130 | `t/auth-jwt.t` | local correctness/infrastructure | JWT signing and validation behavior | 131 + | `t/blob-sync-surfaces.t` | audited local regression | isolated blob upload and sync happy-path coverage for `uploadBlob`, `listBlobs`, `getBlob`, `getLatestCommit`, and `getBlocks` | 131 132 | `t/browser-smoke.t` | local correctness/infrastructure | optional browser-driven end-to-end wrapper | 132 133 | `t/catalog.t` | local correctness/infrastructure | lexicon/catalog exposure smoke | 133 134 | `t/cors.t` | local correctness/infrastructure | CORS and preflight behavior | ··· 138 139 | `t/email-confirmation.t` | audited local regression | intentionally testing-friendly email flow plus strict missing-email and invalid-email validation semantics | 139 140 | `t/email-update-helper.t` | audited local regression | shared email-update helper normalization, token revocation, and duplicate-email error semantics | 140 141 | `t/event-stream.t` | audited local regression | wire-format, malformed frame, and event decoding coverage | 141 - | `t/extended-api.t` | audited local regression | focused mixed coverage for invite issuance, `applyWrites`, identity refresh/update, email flows, and blob/sync happy paths after the label RPCs were split out | 142 + | `t/extended-api.t` | audited local regression | focused mixed coverage for invite issuance, `applyWrites`, identity refresh/update, and email flows after the label and blob/sync happy paths were split out | 142 143 | `t/external-handle-update.t` | audited local regression | external-handle update semantics, including DID-resolution checks and empty-body success for external handle adoption | 143 144 | `t/external-surface.t` | audited local regression | focused external-surface coverage for repo/blob/account-status and missing-blob behavior after splitting discovery and label RPC checks into dedicated suites | 144 145 | `t/firehose.t` | audited local regression | repo subscription lifecycle, cursor, and CAR behavior | ··· 182 183 The broadest suites are green and audited, but they still mix several categories of behavior inside the same file: 183 184 184 185 - `t/extended-api.t` 185 - Carries real conformance value for `applyWrites`, blob/sync flows, and account/email identity lifecycle behavior, but it still mixes those with local product behavior such as self-service invite flows. 186 + Carries real conformance value for `applyWrites` and account/email identity lifecycle behavior, but it still mixes those with local product behavior such as self-service invite flows. 186 187 - `t/external-surface.t` 187 188 Carries strong external-surface coverage for repo export, blob access, account-status behavior, and missing-blob listing. It is cleaner after moving discovery and label-RPC checks into dedicated suites, but still remains broader than a single-endpoint conformance file. 188 189 - `t/uncovered-endpoints.t`
+92
t/blob-sync-surfaces.t
··· 1 + use v5.34; 2 + use warnings; 3 + 4 + use Config (); 5 + use File::Spec; 6 + use File::Temp qw(tempdir); 7 + use FindBin qw($Bin); 8 + use Test::More; 9 + 10 + BEGIN { 11 + require lib; 12 + my $root = File::Spec->rel2abs(File::Spec->catdir($Bin, '..')); 13 + lib->import( 14 + File::Spec->catdir($root, 'lib'), 15 + File::Spec->catdir($root, 'local', 'lib', 'perl5'), 16 + File::Spec->catdir($root, 'local', 'lib', 'perl5', $Config::Config{archname}), 17 + ); 18 + } 19 + 20 + use Test::Mojo; 21 + use ATProto::PDS; 22 + 23 + my $root = File::Spec->rel2abs(File::Spec->catdir($Bin, '..')); 24 + my $tmp = tempdir(CLEANUP => 1); 25 + 26 + my $app = ATProto::PDS->new( 27 + project_root => $root, 28 + settings => { 29 + base_url => 'http://127.0.0.1:7755', 30 + service_handle_domain => 'example.test', 31 + service_did_method => 'did:web', 32 + jwt_secret => 'blob-sync-surface-secret', 33 + admin_password => 'admin-secret', 34 + data_dir => $tmp, 35 + db_path => File::Spec->catfile($tmp, 'perlsky.sqlite'), 36 + }, 37 + ); 38 + 39 + my $t = Test::Mojo->new($app); 40 + 41 + $t->post_ok('/xrpc/com.atproto.server.createAccount' => json => { 42 + handle => 'alice.example.test', 43 + email => 'alice@example.test', 44 + password => 'hunter22', 45 + })->status_is(200); 46 + 47 + my $session = $t->tx->res->json; 48 + my $did = $session->{did}; 49 + my $access = $session->{accessJwt}; 50 + 51 + my $blob_tx = $t->ua->build_tx( 52 + POST => '/xrpc/com.atproto.repo.uploadBlob' => { 53 + Authorization => "Bearer $access", 54 + 'Content-Type' => 'image/png', 55 + } => 'blob-bytes', 56 + ); 57 + $t->request_ok($blob_tx)->status_is(200); 58 + 59 + my $blob = $t->tx->res->json->{blob}; 60 + my $blob_cid = $blob->{ref}{'$link'}; 61 + 62 + $t->post_ok('/xrpc/com.atproto.repo.createRecord' => { 63 + Authorization => "Bearer $access", 64 + } => json => { 65 + repo => $did, 66 + collection => 'com.example.attach', 67 + record => { 68 + '$type' => 'com.example.attach', 69 + blob => $blob, 70 + }, 71 + })->status_is(200); 72 + 73 + $t->get_ok('/xrpc/com.atproto.sync.listBlobs?did=' . $did) 74 + ->status_is(200) 75 + ->json_is('/cids/0', $blob_cid); 76 + 77 + $t->get_ok('/xrpc/com.atproto.sync.getBlob?did=' . $did . '&cid=' . $blob_cid) 78 + ->status_is(200); 79 + is($t->tx->res->body, 'blob-bytes', 'blob bytes are served back'); 80 + like($t->tx->res->headers->content_type // '', qr{image/png}, 'blob content type preserved'); 81 + 82 + $t->get_ok('/xrpc/com.atproto.sync.getLatestCommit?did=' . $did) 83 + ->status_is(200) 84 + ->json_has('/cid'); 85 + 86 + my $commit_cid = $t->tx->res->json->{cid}; 87 + 88 + $t->get_ok('/xrpc/com.atproto.sync.getBlocks?did=' . $did . '&cids=' . $commit_cid) 89 + ->status_is(200); 90 + like($t->tx->res->headers->content_type // '', qr{application/vnd\.ipld\.car}, 'block export is a CAR'); 91 + 92 + done_testing;
-41
t/extended-api.t
··· 214 214 token => $email_confirm->{token}, 215 215 })->status_is(200); 216 216 217 - my $blob_tx = $t->ua->build_tx( 218 - POST => '/xrpc/com.atproto.repo.uploadBlob' => { 219 - Authorization => "Bearer $access", 220 - 'Content-Type' => 'image/png', 221 - } => 'blob-bytes', 222 - ); 223 - $t->request_ok($blob_tx)->status_is(200); 224 - 225 - my $blob = $t->tx->res->json->{blob}; 226 - my $blob_cid = $blob->{ref}{'$link'}; 227 - 228 - $t->post_ok('/xrpc/com.atproto.repo.createRecord' => { 229 - Authorization => "Bearer $access", 230 - } => json => { 231 - repo => $did, 232 - collection => 'com.example.attach', 233 - record => { 234 - '$type' => 'com.example.attach', 235 - blob => $blob, 236 - }, 237 - })->status_is(200); 238 - 239 - $t->get_ok('/xrpc/com.atproto.sync.listBlobs?did=' . $did) 240 - ->status_is(200) 241 - ->json_is('/cids/0', $blob_cid); 242 - 243 - $t->get_ok('/xrpc/com.atproto.sync.getBlob?did=' . $did . '&cid=' . $blob_cid) 244 - ->status_is(200); 245 - is($t->tx->res->body, 'blob-bytes', 'blob bytes are served back'); 246 - like($t->tx->res->headers->content_type // '', qr{image/png}, 'blob content type preserved'); 247 - 248 - $t->get_ok('/xrpc/com.atproto.sync.getLatestCommit?did=' . $did) 249 - ->status_is(200) 250 - ->json_has('/cid'); 251 - 252 - my $commit_cid = $t->tx->res->json->{cid}; 253 - 254 - $t->get_ok('/xrpc/com.atproto.sync.getBlocks?did=' . $did . '&cids=' . $commit_cid) 255 - ->status_is(200); 256 - like($t->tx->res->headers->content_type // '', qr{application/vnd\.ipld\.car}, 'block export is a CAR'); 257 - 258 217 done_testing;