Skip to content

Commit c6d7282

Browse files
authored
Add e2e tests for ActivityPub REST API controllers (#2318)
1 parent bbb2828 commit c6d7282

File tree

7 files changed

+1097
-0
lines changed

7 files changed

+1097
-0
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { test, expect } from '@wordpress/e2e-test-utils-playwright';
5+
6+
test.describe( 'ActivityPub Actors REST API', () => {
7+
let testUserId;
8+
let actorEndpoint;
9+
10+
test.beforeAll( async ( { requestUtils } ) => {
11+
// Use the default test user
12+
testUserId = 1;
13+
actorEndpoint = `/activitypub/1.0/users/${ testUserId }`;
14+
} );
15+
16+
test( 'should return 200 status code for actor endpoint', async ( { requestUtils } ) => {
17+
const data = await requestUtils.rest( {
18+
path: actorEndpoint,
19+
} );
20+
21+
expect( data ).toBeDefined();
22+
} );
23+
24+
test( 'should return valid ActivityStreams Actor object', async ( { requestUtils } ) => {
25+
const data = await requestUtils.rest( {
26+
path: actorEndpoint,
27+
} );
28+
29+
// Check for ActivityStreams context
30+
expect( data ).toHaveProperty( '@context' );
31+
expect( Array.isArray( data[ '@context' ] ) || typeof data[ '@context' ] === 'string' ).toBe( true );
32+
33+
// Verify Actor type
34+
expect( data ).toHaveProperty( 'type' );
35+
expect( [ 'Person', 'Service', 'Organization', 'Application', 'Group' ] ).toContain( data.type );
36+
37+
// Required properties for an Actor
38+
expect( data ).toHaveProperty( 'id' );
39+
expect( data.id ).toMatch( /^https?:\/\// );
40+
41+
expect( data ).toHaveProperty( 'inbox' );
42+
expect( data.inbox ).toMatch( /^https?:\/\// );
43+
44+
expect( data ).toHaveProperty( 'outbox' );
45+
expect( data.outbox ).toMatch( /^https?:\/\// );
46+
} );
47+
48+
test( 'should include endpoints object when available', async ( { requestUtils } ) => {
49+
const data = await requestUtils.rest( {
50+
path: actorEndpoint,
51+
} );
52+
53+
// Endpoints is optional but commonly includes sharedInbox
54+
if ( data.endpoints ) {
55+
expect( typeof data.endpoints ).toBe( 'object' );
56+
if ( data.endpoints.sharedInbox ) {
57+
expect( data.endpoints.sharedInbox ).toMatch( /^https?:\/\// );
58+
}
59+
}
60+
} );
61+
62+
test( 'should include publicKey for verification', async ( { requestUtils } ) => {
63+
const data = await requestUtils.rest( {
64+
path: actorEndpoint,
65+
} );
66+
67+
expect( data ).toHaveProperty( 'publicKey' );
68+
expect( data.publicKey ).toHaveProperty( 'id' );
69+
expect( data.publicKey ).toHaveProperty( 'owner' );
70+
expect( data.publicKey ).toHaveProperty( 'publicKeyPem' );
71+
expect( data.publicKey.publicKeyPem ).toMatch( /^-----BEGIN PUBLIC KEY-----/ );
72+
} );
73+
74+
test( 'should include profile information', async ( { requestUtils } ) => {
75+
const data = await requestUtils.rest( {
76+
path: actorEndpoint,
77+
} );
78+
79+
// Optional but commonly present properties
80+
if ( data.name ) {
81+
expect( typeof data.name ).toBe( 'string' );
82+
}
83+
84+
if ( data.preferredUsername ) {
85+
expect( typeof data.preferredUsername ).toBe( 'string' );
86+
}
87+
88+
if ( data.summary ) {
89+
expect( typeof data.summary ).toBe( 'string' );
90+
}
91+
92+
if ( data.url ) {
93+
expect( data.url ).toMatch( /^https?:\/\// );
94+
}
95+
} );
96+
97+
test( 'should return error for non-existent user', async ( { requestUtils } ) => {
98+
try {
99+
await requestUtils.rest( {
100+
path: '/activitypub/1.0/users/999999',
101+
} );
102+
// If we reach here, the test should fail
103+
expect.fail();
104+
} catch ( error ) {
105+
// Should return 400 or 404 for invalid/non-existent user
106+
expect( [ 400, 404 ] ).toContain( error.status || error.code );
107+
}
108+
} );
109+
110+
test( 'should return correct Content-Type header', async ( { requestUtils } ) => {
111+
const data = await requestUtils.rest( {
112+
path: actorEndpoint,
113+
} );
114+
115+
expect( data ).toBeDefined();
116+
expect( data ).toHaveProperty( 'type' );
117+
} );
118+
119+
test( 'should validate followers and following collections are present', async ( { requestUtils } ) => {
120+
const data = await requestUtils.rest( {
121+
path: actorEndpoint,
122+
} );
123+
124+
expect( data ).toHaveProperty( 'followers' );
125+
expect( data.followers ).toMatch( /^https?:\/\// );
126+
127+
expect( data ).toHaveProperty( 'following' );
128+
expect( data.following ).toMatch( /^https?:\/\// );
129+
} );
130+
131+
test( 'should include featured collection', async ( { requestUtils } ) => {
132+
const data = await requestUtils.rest( {
133+
path: actorEndpoint,
134+
} );
135+
136+
if ( data.featured ) {
137+
expect( data.featured ).toMatch( /^https?:\/\// );
138+
}
139+
} );
140+
} );
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { test, expect } from '@wordpress/e2e-test-utils-playwright';
5+
6+
test.describe( 'ActivityPub Following Collection REST API', () => {
7+
let testUserId;
8+
let followingEndpoint;
9+
10+
test.beforeAll( async ( { requestUtils } ) => {
11+
// Use the default test user
12+
testUserId = 1;
13+
followingEndpoint = `/activitypub/1.0/users/${ testUserId }/following`;
14+
} );
15+
16+
test( 'should return 200 status code for following endpoint', async ( { requestUtils } ) => {
17+
const data = await requestUtils.rest( {
18+
path: followingEndpoint,
19+
} );
20+
21+
expect( data ).toBeDefined();
22+
} );
23+
24+
test( 'should return ActivityStreams OrderedCollection', async ( { requestUtils } ) => {
25+
const data = await requestUtils.rest( {
26+
path: followingEndpoint,
27+
} );
28+
29+
// Check for ActivityStreams context
30+
expect( data ).toHaveProperty( '@context' );
31+
expect( Array.isArray( data[ '@context' ] ) || typeof data[ '@context' ] === 'string' ).toBe( true );
32+
33+
// Verify it's an OrderedCollection
34+
expect( data.type ).toBe( 'OrderedCollection' );
35+
36+
// Check for required collection properties
37+
expect( data ).toHaveProperty( 'id' );
38+
expect( data.id ).toMatch( /^https?:\/\// );
39+
40+
expect( data ).toHaveProperty( 'totalItems' );
41+
expect( typeof data.totalItems ).toBe( 'number' );
42+
} );
43+
44+
test( 'should handle empty following collection', async ( { requestUtils } ) => {
45+
const data = await requestUtils.rest( {
46+
path: followingEndpoint,
47+
} );
48+
49+
// For a new user, following should be 0
50+
if ( data.totalItems === 0 ) {
51+
expect( data.orderedItems ).toEqual( [] );
52+
}
53+
} );
54+
55+
test( 'should include first property for pagination', async ( { requestUtils } ) => {
56+
const data = await requestUtils.rest( {
57+
path: followingEndpoint,
58+
} );
59+
60+
if ( data.totalItems > 0 ) {
61+
expect( data ).toHaveProperty( 'first' );
62+
63+
if ( typeof data.first === 'string' ) {
64+
expect( data.first ).toMatch( /^https?:\/\// );
65+
} else if ( typeof data.first === 'object' ) {
66+
expect( data.first.type ).toBe( 'OrderedCollectionPage' );
67+
expect( data.first ).toHaveProperty( 'orderedItems' );
68+
}
69+
}
70+
} );
71+
72+
test( 'should return error for non-existent user', async ( { requestUtils } ) => {
73+
try {
74+
await requestUtils.rest( {
75+
path: '/activitypub/1.0/users/999999/following',
76+
} );
77+
// If we reach here, the test should fail
78+
expect.fail();
79+
} catch ( error ) {
80+
// Should return 400 or 404 for invalid/non-existent user
81+
expect( [ 400, 404 ] ).toContain( error.status || error.code );
82+
}
83+
} );
84+
85+
test( 'should return correct Content-Type header', async ( { requestUtils } ) => {
86+
const data = await requestUtils.rest( {
87+
path: followingEndpoint,
88+
} );
89+
90+
expect( data ).toBeDefined();
91+
expect( data ).toHaveProperty( 'type' );
92+
} );
93+
94+
test( 'should handle page parameter', async ( { requestUtils } ) => {
95+
try {
96+
const data = await requestUtils.rest( {
97+
path: `${ followingEndpoint }?page=1`,
98+
} );
99+
100+
// If successful, verify the response structure
101+
expect( data.type ).toBe( 'OrderedCollectionPage' );
102+
} catch ( error ) {
103+
// Skip this test if pagination isn't available yet
104+
expect( error.status || error.code ).toBeGreaterThanOrEqual( 400 );
105+
}
106+
} );
107+
108+
test( 'should validate collection structure matches ActivityStreams spec', async ( { requestUtils } ) => {
109+
const data = await requestUtils.rest( {
110+
path: followingEndpoint,
111+
} );
112+
113+
// Check for required ActivityStreams properties
114+
expect( data ).toHaveProperty( '@context' );
115+
expect( Array.isArray( data[ '@context' ] ) || typeof data[ '@context' ] === 'string' ).toBe( true );
116+
117+
// Verify ID is a valid URL
118+
expect( data.id ).toMatch( /^https?:\/\// );
119+
120+
// Verify proper typing
121+
expect( data.type ).toBe( 'OrderedCollection' );
122+
} );
123+
124+
test( 'should validate orderedItems structure when present', async ( { requestUtils } ) => {
125+
const data = await requestUtils.rest( {
126+
path: followingEndpoint,
127+
} );
128+
129+
if ( data.orderedItems && data.orderedItems.length > 0 ) {
130+
// Each item should be either a URL string or an object
131+
data.orderedItems.forEach( ( item ) => {
132+
if ( typeof item === 'string' ) {
133+
expect( item ).toMatch( /^https?:\/\// );
134+
} else if ( typeof item === 'object' ) {
135+
expect( item ).toHaveProperty( 'type' );
136+
expect( item ).toHaveProperty( 'id' );
137+
}
138+
} );
139+
}
140+
} );
141+
} );

0 commit comments

Comments
 (0)