Skip to content

Commit fc17048

Browse files
Fix getCodeInfo() URL-encoding mismatch breaking CWV/a11y code-fix for refs with /
1 parent 24e70af commit fc17048

2 files changed

Lines changed: 106 additions & 2 deletions

File tree

src/accessibility/utils/data-processing.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,11 +1172,13 @@ export async function getCodeInfo(site, opportunityType, context) {
11721172
}
11731173

11741174
const {
1175-
type: source, owner, repo, ref,
1175+
type: source, owner, repo, ref, s3StoragePath,
11761176
} = codeConfig;
11771177

11781178
const codeBucket = env.S3_IMPORTER_BUCKET_NAME;
1179-
const codePath = `code/${siteId}/${source}/${owner}/${repo}/${ref}/repository.zip`;
1179+
// Mirror the import-worker's encodeURIComponent(ref) so refs with '/' resolve.
1180+
const codePath = s3StoragePath
1181+
|| `code/${siteId}/${source}/${owner}/${repo}/${encodeURIComponent(ref)}/repository.zip`;
11801182

11811183
// Verify if the file exists in S3 bucket
11821184
let fileExists = false;

test/audits/accessibility/data-processing.test.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7381,5 +7381,107 @@ describe('data-processing utility functions', () => {
73817381
});
73827382
});
73837383
});
7384+
7385+
describe('Code config branch (imported archive)', () => {
7386+
beforeEach(() => {
7387+
// Force the imported-archive branch by making delivery type non-aem_edge
7388+
// and providing a populated code config.
7389+
mockSite.getDeliveryType = () => 'aem_cs';
7390+
mockS3Client.send.resolves();
7391+
});
7392+
7393+
// Refs containing URL-special characters must be encoded so the HEAD check
7394+
// targets the same key the import-worker actually wrote (which uses
7395+
// encodeURIComponent on the ref segment).
7396+
const encodedRefCases = [
7397+
{ ref: 'release/2.03.0', encoded: 'release%2F2.03.0' },
7398+
{ ref: 'feature/foo', encoded: 'feature%2Ffoo' },
7399+
{ ref: 'release/26-March-2026-PROD', encoded: 'release%2F26-March-2026-PROD' },
7400+
{ ref: 'topic#hot', encoded: 'topic%23hot' },
7401+
{ ref: 'spaced ref', encoded: 'spaced%20ref' },
7402+
{ ref: 'q?uery', encoded: 'q%3Fuery' },
7403+
];
7404+
7405+
encodedRefCases.forEach(({ ref, encoded }) => {
7406+
it(`encodes ref "${ref}" when reconstructing codePath`, async () => {
7407+
mockSite.getCode = () => ({
7408+
type: 'github', owner: 'o', repo: 'r', ref,
7409+
});
7410+
7411+
const result = await getCodeInfo(mockSite, 'cwv', mockContext);
7412+
7413+
const expectedPath = `code/site-123/github/o/r/${encoded}/repository.zip`;
7414+
expect(result).to.deep.equal({
7415+
codeBucket: 'importer-bucket',
7416+
codePath: expectedPath,
7417+
});
7418+
expect(mockS3Client.send).to.have.been.calledOnce;
7419+
const cmd = mockS3Client.send.firstCall.args[0];
7420+
expect(cmd).to.be.instanceOf(HeadObjectCommand);
7421+
expect(cmd.input).to.deep.equal({
7422+
Bucket: 'importer-bucket',
7423+
Key: expectedPath,
7424+
});
7425+
});
7426+
});
7427+
7428+
it('leaves refs without special characters unchanged', async () => {
7429+
mockSite.getCode = () => ({
7430+
type: 'github', owner: 'o', repo: 'r', ref: 'main',
7431+
});
7432+
7433+
const result = await getCodeInfo(mockSite, 'cwv', mockContext);
7434+
7435+
expect(result.codePath).to.equal('code/site-123/github/o/r/main/repository.zip');
7436+
});
7437+
7438+
it('prefers s3StoragePath from site.code when present (Option B)', async () => {
7439+
mockSite.getCode = () => ({
7440+
type: 'github',
7441+
owner: 'o',
7442+
repo: 'r',
7443+
ref: 'release/2.03.0',
7444+
s3StoragePath: 'code/site-123/github/o/r/release%2F2.03.0/repository.zip',
7445+
});
7446+
7447+
const result = await getCodeInfo(mockSite, 'cwv', mockContext);
7448+
7449+
expect(result).to.deep.equal({
7450+
codeBucket: 'importer-bucket',
7451+
codePath: 'code/site-123/github/o/r/release%2F2.03.0/repository.zip',
7452+
});
7453+
// HEAD check still runs against the stored key
7454+
expect(mockS3Client.send.firstCall.args[0].input.Key)
7455+
.to.equal('code/site-123/github/o/r/release%2F2.03.0/repository.zip');
7456+
});
7457+
7458+
it('uses the encoded fallback when s3StoragePath is missing', async () => {
7459+
// Simulates a site imported before s3StoragePath was populated.
7460+
mockSite.getCode = () => ({
7461+
type: 'github', owner: 'o', repo: 'r', ref: 'release/2.03.0',
7462+
});
7463+
7464+
const result = await getCodeInfo(mockSite, 'cwv', mockContext);
7465+
7466+
expect(result.codePath)
7467+
.to.equal('code/site-123/github/o/r/release%2F2.03.0/repository.zip');
7468+
});
7469+
7470+
it('returns null for non-aem_edge when the archive is missing in S3', async () => {
7471+
const notFound = new Error('Not Found');
7472+
notFound.name = 'NotFound';
7473+
mockS3Client.send.rejects(notFound);
7474+
mockSite.getCode = () => ({
7475+
type: 'github', owner: 'o', repo: 'r', ref: 'release/2.03.0',
7476+
});
7477+
7478+
const result = await getCodeInfo(mockSite, 'cwv', mockContext);
7479+
7480+
expect(result).to.be.null;
7481+
expect(mockLog.warn).to.have.been.calledWith(
7482+
sinon.match(/release%2F2\.03\.0\/repository\.zip/),
7483+
);
7484+
});
7485+
});
73847486
});
73857487
});

0 commit comments

Comments
 (0)