OverviewFilesCommitsIssuesDocsReleasesPeers

ap-bridge commits / 471aad5

test: pin banner -> AS2 image, and that neither picture invents the other
The projection was the one link in the banner chain nothing exercised, and the alternative to a test was putting a placeholder image on a live public profile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
471aad52b1d41591c05c040f3a87af980daf1c40
by plankatron <fleeky@gmail.com> · 2026-09-09T22:51:46.000Z

Changed files (1) +35 -0

test/favela-source.test.js +35 -0
……@@ -131,3 +131,38 @@ test('⚠ a post with no picture carries no attachment key', async (t) => {
131131 const { notes } = await src({ networks: ['fediverse'] }, [row('plain')]).notes()
132132 t.absent(notes[0].attachment && notes[0].attachment.length)
133133})
134
135// ⛔ `image` IS THE BANNER and `icon` IS THE AVATAR — two AS2 fields, two different pictures.
136// Nothing here emitted `image` because favela's profile record had no banner field until
137// favela-peer 0.0.49, so every actor this bridge served advertised no header and Mastodon rendered
138// its own placeholder (hyper-social #67 §7.6). buildActor has always passed `profile.image`
139// through, so this was the only missing link.
140test('getProfile projects a banner oid as AS2 image, independently of the avatar', async (t) => {
141 const mk = (frontmatter) => new FavelaSource({
142 peer: {
143 async profile () { return { frontmatter, body: 'about me' } },
144 async reach () { return { networks: ['fediverse'] } },
145 async ownPosts () { return { rows: [] } }
146 },
147 host: 'ap.7rnx.net',
148 base: 'https://ap.7rnx.net/ap/users/ashatron',
149 actorId: 'https://ap.7rnx.net/ap/users/ashatron/actor',
150 followersId: 'https://ap.7rnx.net/ap/users/ashatron/followers'
151 })
152
153 const both = await mk({ name: 'ash', avatar: 'a'.repeat(64), banner: 'b'.repeat(64) }).getProfile()
154 t.ok(both.icon.url.endsWith('a'.repeat(64)), 'the avatar is the icon')
155 t.ok(both.image.url.endsWith('b'.repeat(64)), '⛔ and the banner is the image, not a second icon')
156 t.is(both.image.type, 'Image')
157 // Both hang off THIS user's base, never the primary's flat path.
158 t.ok(both.image.url.startsWith('https://ap.7rnx.net/ap/users/ashatron/blobs/'), 'under their own root')
159
160 // Each is independent: a profile with only one must not invent the other.
161 const onlyAvatar = await mk({ avatar: 'a'.repeat(64) }).getProfile()
162 t.ok(onlyAvatar.icon, 'avatar alone still projects')
163 t.absent(onlyAvatar.image, 'and no banner is invented')
164
165 const onlyBanner = await mk({ banner: 'b'.repeat(64) }).getProfile()
166 t.ok(onlyBanner.image, 'banner alone still projects')
167 t.absent(onlyBanner.icon, 'and no avatar is invented')
168})