Skip to content

Commit 4302d3f

Browse files
committed
feat(maven): Add Central repository support
1 parent bf57d0d commit 4302d3f

2 files changed

Lines changed: 164 additions & 78 deletions

File tree

src/targets/__tests__/maven.test.ts

Lines changed: 108 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
POM_DEFAULT_FILENAME,
99
targetOptions,
1010
targetSecrets,
11+
CENTRAL_API_BASE_URL,
1112
} from '../maven';
1213
import { retrySpawnProcess, sleep } from '../../utils/async';
1314
import { withTempDir } from '../../utils/files';
@@ -104,13 +105,12 @@ function createMavenTarget(
104105
}
105106

106107
function getRepositoryInfo(
107-
type: NexusRepository['type'],
108-
transitioning: NexusRepository['transitioning']
108+
state: NexusRepository['state'],
109109
): NexusRepository {
110110
return {
111-
type,
111+
state,
112112
repositoryId: 'sentry-java',
113-
transitioning,
113+
deploymentId: '1234',
114114
};
115115
}
116116

@@ -537,15 +537,15 @@ describe('closeAndReleaseRepository', () => {
537537
test('should throw if repository is not opened', async () => {
538538
const mvnTarget = createMavenTarget();
539539
mvnTarget.getRepository = jest.fn(() =>
540-
Promise.resolve(getRepositoryInfo('closed', false))
540+
Promise.resolve(getRepositoryInfo('closed'))
541541
);
542542
await expect(mvnTarget.closeAndReleaseRepository()).rejects.toThrow();
543543
});
544544

545545
test('should call closeRepository and releaseRepository with fetched repository ID', async () => {
546546
const mvnTarget = createMavenTarget();
547547
mvnTarget.getRepository = jest.fn(() =>
548-
Promise.resolve(getRepositoryInfo('open', false))
548+
Promise.resolve(getRepositoryInfo('open'))
549549
);
550550
const callOrder: string[] = [];
551551
mvnTarget.closeRepository = jest.fn(async () => {
@@ -567,7 +567,7 @@ describe('closeAndReleaseRepository', () => {
567567
test('should not release repostiory if it was not closed properly', async () => {
568568
const mvnTarget = createMavenTarget();
569569
mvnTarget.getRepository = jest.fn(() =>
570-
Promise.resolve(getRepositoryInfo('open', false))
570+
Promise.resolve(getRepositoryInfo('open'))
571571
);
572572
mvnTarget.closeRepository = jest.fn(() => Promise.reject());
573573
mvnTarget.releaseRepository = jest.fn(() => Promise.resolve(true));
@@ -586,13 +586,13 @@ describe('closeAndReleaseRepository', () => {
586586

587587
describe('getRepository', () => {
588588
const url = new URL(NEXUS_API_BASE_URL);
589-
const repositoryInfo = getRepositoryInfo('open', false);
589+
const repositoryInfo = getRepositoryInfo('open');
590590

591591
test('should return the repository if server responds correctly', async () => {
592592
nock(url.origin)
593-
.get(`${url.pathname}/profile_repositories`)
593+
.get('/manual/search/repositories')
594594
.reply(200, {
595-
data: [repositoryInfo],
595+
repositories: [{key: 'sentry-java', state: 'open', portal_deployment_id: '1234'}],
596596
});
597597

598598
const mvnTarget = createMavenTarget();
@@ -602,8 +602,8 @@ describe('getRepository', () => {
602602
});
603603

604604
test('should throw if server returns no active repositories', async () => {
605-
nock(url.origin).get(`${url.pathname}/profile_repositories`).reply(200, {
606-
data: [],
605+
nock(url.origin).get('/manual/search/repositories').reply(200, {
606+
repositories: [],
607607
});
608608

609609
const mvnTarget = createMavenTarget();
@@ -614,9 +614,9 @@ describe('getRepository', () => {
614614

615615
test('should throw if server returns more than one active repository', async () => {
616616
nock(url.origin)
617-
.get(`${url.pathname}/profile_repositories`)
617+
.get('/manual/search/repositories')
618618
.reply(200, {
619-
data: [repositoryInfo, repositoryInfo],
619+
repositories: [repositoryInfo, repositoryInfo],
620620
});
621621

622622
const mvnTarget = createMavenTarget();
@@ -628,7 +628,7 @@ describe('getRepository', () => {
628628
});
629629

630630
test('should throw if server doesnt accept the request', async () => {
631-
nock(url.origin).get(`${url.pathname}/profile_repositories`).reply(500);
631+
nock(url.origin).get('/manual/search/repositories').reply(500);
632632

633633
const mvnTarget = createMavenTarget();
634634
await expect(mvnTarget.getRepository()).rejects.toThrow(
@@ -643,23 +643,23 @@ describe('closeRepository', () => {
643643

644644
test('should return true if server responds correctly', async () => {
645645
nock(url.origin)
646-
.post(`${url.pathname}/bulk/close`, {
647-
data: { stagedRepositoryIds: [repositoryId] },
646+
.post('/service/local/staging/bulk/close', {
647+
data: { stagedRepositoryIds: [repositoryId], description: '', autoDropAfterRelease: true },
648648
})
649649
.reply(200);
650650

651651
const mvnTarget = createMavenTarget();
652652
mvnTarget.getRepository = jest.fn(() =>
653-
Promise.resolve(getRepositoryInfo('closed', false))
653+
Promise.resolve(getRepositoryInfo('closed'))
654654
);
655655

656656
await expect(mvnTarget.closeRepository(repositoryId)).resolves.toBe(true);
657657
});
658658

659659
test('should throw if server doesnt accept the request', async () => {
660660
nock(url.origin)
661-
.post(`${url.pathname}/bulk/close`, {
662-
data: { stagedRepositoryIds: [repositoryId] },
661+
.post('/service/local/staging/bulk/close', {
662+
data: { stagedRepositoryIds: [repositoryId], description: '', autoDropAfterRelease: true },
663663
})
664664
.reply(500);
665665

@@ -673,47 +673,22 @@ describe('closeRepository', () => {
673673

674674
test('should wait for the status of repository to be changed', async () => {
675675
nock(url.origin)
676-
.post(`${url.pathname}/bulk/close`, {
677-
data: { stagedRepositoryIds: [repositoryId] },
676+
.post('/service/local/staging/bulk/close', {
677+
data: { stagedRepositoryIds: [repositoryId], description: '', autoDropAfterRelease: true },
678678
})
679679
.reply(200);
680680

681681
const mvnTarget = createMavenTarget();
682682
mvnTarget.getRepository = jest
683683
.fn()
684684
.mockImplementationOnce(() =>
685-
Promise.resolve(getRepositoryInfo('open', false))
685+
Promise.resolve(getRepositoryInfo('open'))
686686
)
687687
.mockImplementationOnce(() =>
688-
Promise.resolve(getRepositoryInfo('open', false))
688+
Promise.resolve(getRepositoryInfo('open'))
689689
)
690690
.mockImplementationOnce(() =>
691-
Promise.resolve(getRepositoryInfo('closed', false))
692-
);
693-
694-
await expect(mvnTarget.closeRepository(repositoryId)).resolves.toBe(true);
695-
expect(sleep).toHaveBeenCalledTimes(3);
696-
expect(mvnTarget.getRepository).toHaveBeenCalledTimes(3);
697-
});
698-
699-
test('should wait for the repository to not be in transitioning state', async () => {
700-
nock(url.origin)
701-
.post(`${url.pathname}/bulk/close`, {
702-
data: { stagedRepositoryIds: [repositoryId] },
703-
})
704-
.reply(200);
705-
706-
const mvnTarget = createMavenTarget();
707-
mvnTarget.getRepository = jest
708-
.fn()
709-
.mockImplementationOnce(() =>
710-
Promise.resolve(getRepositoryInfo('closed', true))
711-
)
712-
.mockImplementationOnce(() =>
713-
Promise.resolve(getRepositoryInfo('closed', true))
714-
)
715-
.mockImplementationOnce(() =>
716-
Promise.resolve(getRepositoryInfo('closed', false))
691+
Promise.resolve(getRepositoryInfo('closed'))
717692
);
718693

719694
await expect(mvnTarget.closeRepository(repositoryId)).resolves.toBe(true);
@@ -723,14 +698,14 @@ describe('closeRepository', () => {
723698

724699
test('should throw when status change deadline is reached', async () => {
725700
nock(url.origin)
726-
.post(`${url.pathname}/bulk/close`, {
727-
data: { stagedRepositoryIds: [repositoryId] },
701+
.post('/service/local/staging/bulk/close', {
702+
data: { stagedRepositoryIds: [repositoryId], description: '', autoDropAfterRelease: true },
728703
})
729704
.reply(200);
730705

731706
const mvnTarget = createMavenTarget();
732707
mvnTarget.getRepository = jest.fn(() =>
733-
Promise.resolve(getRepositoryInfo('open', false))
708+
Promise.resolve(getRepositoryInfo('open'))
734709
);
735710

736711
// Deadline is 60min, so we fake pooling start time and initial read to 1min
@@ -751,31 +726,107 @@ describe('closeRepository', () => {
751726

752727
describe('releaseRepository', () => {
753728
const url = new URL(NEXUS_API_BASE_URL);
729+
const centralUrl = new URL(CENTRAL_API_BASE_URL);
754730
const repositoryId = 'sentry-java';
731+
const deploymentId = '1234';
755732

756733
test('should return true if server responds correctly', async () => {
757734
nock(url.origin)
758-
.post(`${url.pathname}/bulk/promote`, {
759-
data: { stagedRepositoryIds: [repositoryId] },
735+
.post('/service/local/staging/bulk/promote', {
736+
data: { stagedRepositoryIds: [repositoryId], description: '', autoDropAfterRelease: true },
760737
})
761738
.reply(200);
762739

740+
nock(centralUrl.origin)
741+
.post(`${centralUrl.pathname}/publisher/status?id=${deploymentId}`)
742+
.reply(200, {
743+
deploymentState: 'PUBLISHED'
744+
});
745+
763746
const mvnTarget = createMavenTarget();
747+
mvnTarget.getRepository = jest.fn(() =>
748+
Promise.resolve(getRepositoryInfo('closed'))
749+
);
764750
await expect(mvnTarget.releaseRepository(repositoryId)).resolves.toBe(true);
765751
});
766752

767753
test('should throw if server doesnt accept the request', async () => {
768754
nock(url.origin)
769-
.post(`${url.pathname}/bulk/promote`, {
770-
data: { stagedRepositoryIds: [repositoryId] },
755+
.post('/service/local/staging/bulk/promote', {
756+
data: { stagedRepositoryIds: [repositoryId], description: '', autoDropAfterRelease: true },
771757
})
772758
.reply(500);
773759

774760
const mvnTarget = createMavenTarget();
761+
mvnTarget.getRepository = jest.fn(() =>
762+
Promise.resolve(getRepositoryInfo('closed'))
763+
);
775764
await expect(mvnTarget.releaseRepository(repositoryId)).rejects.toThrow(
776765
new Error(
777766
'Unable to release repository sentry-java: 500, Internal Server Error'
778767
)
779768
);
780769
});
770+
771+
test('should wait for the status of deployment to be changed', async () => {
772+
nock(url.origin)
773+
.post('/service/local/staging/bulk/promote', {
774+
data: { stagedRepositoryIds: [repositoryId], description: '', autoDropAfterRelease: true },
775+
})
776+
.reply(200);
777+
778+
const mvnTarget = createMavenTarget();
779+
mvnTarget.getRepository = jest.fn(() =>
780+
Promise.resolve(getRepositoryInfo('closed'))
781+
);
782+
783+
nock(centralUrl.origin)
784+
.post(`${centralUrl.pathname}/publisher/status?id=${deploymentId}`)
785+
.reply(200, {
786+
deploymentState: 'VALIDATED'
787+
})
788+
.post(`${centralUrl.pathname}/publisher/status?id=${deploymentId}`)
789+
.reply(200, {
790+
deploymentState: 'PUBLISHING'
791+
})
792+
.post(`${centralUrl.pathname}/publisher/status?id=${deploymentId}`)
793+
.reply(200, {
794+
deploymentState: 'PUBLISHED'
795+
});
796+
797+
await expect(mvnTarget.releaseRepository(repositoryId)).resolves.toBe(true);
798+
});
799+
800+
test('should throw if deadline is reached', async () => {
801+
nock(url.origin)
802+
.post('/service/local/staging/bulk/promote', {
803+
data: { stagedRepositoryIds: [repositoryId], description: '', autoDropAfterRelease: true },
804+
})
805+
.reply(200);
806+
807+
const mvnTarget = createMavenTarget();
808+
mvnTarget.getRepository = jest.fn(() =>
809+
Promise.resolve(getRepositoryInfo('closed'))
810+
);
811+
812+
nock(centralUrl.origin)
813+
.post(`${centralUrl.pathname}/publisher/status?id=${deploymentId}`)
814+
.reply(200, {
815+
deploymentState: 'PUBLISHING'
816+
})
817+
818+
// Deadline is 60min, so we fake pooling start time and initial read to 1min
819+
// and second iteration to something over 60min
820+
jest
821+
.spyOn(Date, 'now')
822+
.mockImplementationOnce(() => 1 * 60 * 1000)
823+
.mockImplementationOnce(() => 1 * 60 * 1000)
824+
.mockImplementationOnce(() => 62 * 60 * 1000);
825+
826+
await expect(mvnTarget.releaseRepository(repositoryId)).rejects.toThrow(
827+
new Error('Deadline for Central repository status change reached.')
828+
);
829+
expect(sleep).toHaveBeenCalledTimes(1);
830+
expect(mvnTarget.getRepository).toHaveBeenCalledTimes(1);
831+
});
781832
});

0 commit comments

Comments
 (0)