Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions clients/passkeys-browser/test/admin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,21 +88,6 @@ describe("PkAuthAdminClient", () => {
expect(updated.label).toBe("new");
});

it("removeCredential DELETEs the credential", async () => {
const fetchImpl = stubFetch({
"/auth/admin/credentials/abc": (init) => {
expect(init.method).toBe("DELETE");
return { status: 204, body: "" };
},
});
const c = new PkAuthAdminClient({
apiBase: "https://x",
getToken: () => "t",
fetch: fetchImpl as unknown as typeof fetch,
});
await c.removeCredential("abc");
});

it("regenerateBackupCodes POSTs and returns the batch", async () => {
const fetchImpl = stubFetch({
"/auth/admin/backup-codes/regenerate": (init) => {
Expand Down
4 changes: 0 additions & 4 deletions examples/dropwizard-demo/e2e/tests/ceremony.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ async function installVirtualAuthenticator(
return { authenticatorId: result.authenticatorId };
}

async function readOut(page: Page, id: string): Promise<string> {
return (await page.locator(`#${id}`).textContent()) ?? "";
}

test.describe("pk-auth Dropwizard demo — full flow", () => {
test.beforeEach(async ({ page }) => {
await installVirtualAuthenticator(page);
Expand Down
4 changes: 0 additions & 4 deletions examples/micronaut-demo/e2e/tests/ceremony.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ async function installVirtualAuthenticator(
return { authenticatorId: result.authenticatorId };
}

async function readOut(page: Page, id: string): Promise<string> {
return (await page.locator(`#${id}`).textContent()) ?? "";
}

test.describe("pk-auth Micronaut demo — full flow", () => {
test.beforeEach(async ({ page }) => {
await installVirtualAuthenticator(page);
Expand Down
4 changes: 0 additions & 4 deletions examples/spring-boot-demo/e2e/tests/ceremony.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ async function installVirtualAuthenticator(
return { authenticatorId: result.authenticatorId };
}

async function readOut(page: Page, id: string): Promise<string> {
return (await page.locator(`#${id}`).textContent()) ?? "";
}

test.describe("pk-auth Spring Boot demo — full flow", () => {
test.beforeEach(async ({ page }) => {
await installVirtualAuthenticator(page);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,6 @@
*/
class AdminResponseShapesTest {

@Test
void backupCodesCountResponseCarriesRemaining() {
assertThat(new BackupCodesCountResponse(7).remaining()).isEqualTo(7);
assertThat(new BackupCodesCountResponse(0).remaining()).isZero();
}

@Test
void emailVerificationResultCarriesUserHandle() {
UserHandle uh = UserHandle.of(new byte[] {1, 2, 3});
assertThat(new EmailVerificationResult(uh).userHandle()).isEqualTo(uh);
}

@Test
void emailVerificationResultRejectsNullHandle() {
assertThatThrownBy(() -> new EmailVerificationResult(null))
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,126 +1,32 @@
// SPDX-License-Identifier: MIT
package com.codeheadsystems.pkauth.api;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import com.codeheadsystems.pkauth.credential.AuthenticatorData;
import com.codeheadsystems.pkauth.credential.CredentialRecord;
import java.time.Instant;
import java.util.Set;
import org.junit.jupiter.api.Test;

/**
* Compact-constructor validation for the sealed ceremony result variants. Accessor read-backs and
* exhaustive-switch coverage are exercised by the ceremony service tests that actually produce
* these results; only the hand-written validation guards are tested here.
*/
class ResultTypesTest {

@Test
void registrationResultSuccessConstruction() {
UserHandle uh = UserHandle.random();
CredentialRecord cred =
new CredentialRecord(
CredentialId.of(new byte[] {1, 2, 3}),
uh,
new byte[] {4, 5},
0L,
"Yubikey",
null,
Set.of(Transport.USB),
true,
true,
Instant.now(),
null);
AuthenticatorData authData =
new AuthenticatorData(new byte[] {1}, true, true, false, false, true, false, 0L);

RegistrationResult result = new RegistrationResult.Success(cred, authData);

String formatted =
switch (result) {
case RegistrationResult.Success s -> "ok:" + s.credential().label();
case RegistrationResult.InvalidChallenge ic -> "ic:" + ic.detail();
case RegistrationResult.OriginMismatch om -> "om:" + om.expected();
case RegistrationResult.AttestationRejected ar -> "ar:" + ar.reason();
case RegistrationResult.DuplicateCredential dc -> "dup";
case RegistrationResult.InvalidPayload ip -> "ip:" + ip.detail();
case RegistrationResult.RateLimited rl -> "rl:" + rl.bucket();
};
assertThat(formatted).isEqualTo("ok:Yubikey");
}

@Test
void registrationResultVariantsBuildAndValidate() {
assertThat(new RegistrationResult.InvalidChallenge("nope").detail()).isEqualTo("nope");
assertThat(new RegistrationResult.OriginMismatch("a", "b").actual()).isEqualTo("b");
assertThat(new RegistrationResult.AttestationRejected("fail").reason()).isEqualTo("fail");
assertThat(new RegistrationResult.InvalidPayload("bad").detail()).isEqualTo("bad");

CredentialId credId = CredentialId.of(new byte[] {7, 8, 9});
RegistrationResult.DuplicateCredential dup = new RegistrationResult.DuplicateCredential(credId);
assertThat(dup.credentialId()).isEqualTo(credId);
assertThat(dup)
.isEqualTo(
new RegistrationResult.DuplicateCredential(CredentialId.of(new byte[] {7, 8, 9})))
.hasSameHashCodeAs(
new RegistrationResult.DuplicateCredential(CredentialId.of(new byte[] {7, 8, 9})));

void registrationResultInvalidChallengeRejectsNullDetail() {
assertThatThrownBy(() -> new RegistrationResult.InvalidChallenge(null))
.isInstanceOf(NullPointerException.class);
}

@Test
void assertionResultSuccessConstructionAndAccessors() {
UserHandle uh = UserHandle.random();
CredentialId credId = CredentialId.of(new byte[] {10, 11});
AssertionResult.Success success =
new AssertionResult.Success(uh, credId, 42L, AssertionResult.CounterStatus.OK);

assertThat(success.userHandle()).isEqualTo(uh);
assertThat(success.credentialId()).isEqualTo(credId);
assertThat(success.signCount()).isEqualTo(42L);
assertThat(success.counterStatus()).isEqualTo(AssertionResult.CounterStatus.OK);
assertThat(success)
.isEqualTo(
new AssertionResult.Success(
uh, CredentialId.of(new byte[] {10, 11}), 42L, AssertionResult.CounterStatus.OK))
.hasSameHashCodeAs(
new AssertionResult.Success(
uh, CredentialId.of(new byte[] {10, 11}), 42L, AssertionResult.CounterStatus.OK));
void assertionResultSuccessRejectsNegativeSignCount() {
assertThatThrownBy(
() ->
new AssertionResult.Success(
uh, CredentialId.of(new byte[] {0}), -1, AssertionResult.CounterStatus.OK))
UserHandle.random(),
CredentialId.of(new byte[] {0}),
-1,
AssertionResult.CounterStatus.OK))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void assertionResultVariantsExhaustiveSwitch() {
AssertionResult[] cases = {
new AssertionResult.UnknownCredential(CredentialId.of(new byte[] {1})),
new AssertionResult.InvalidChallenge("x"),
new AssertionResult.OriginMismatch("a", "b"),
new AssertionResult.CounterRegression(5, 4),
new AssertionResult.UserVerificationRequired(),
new AssertionResult.InvalidSignature(),
};
for (AssertionResult r : cases) {
String tag =
switch (r) {
case AssertionResult.Success s -> "success";
case AssertionResult.UnknownCredential uc ->
"unknown:" + uc.credentialId().value().length;
case AssertionResult.InvalidChallenge ic -> "ic";
case AssertionResult.OriginMismatch om -> "om";
case AssertionResult.CounterRegression cr -> "cr:" + cr.stored() + "/" + cr.received();
case AssertionResult.UserVerificationRequired uvr -> "uvr";
case AssertionResult.InvalidSignature is -> "is";
case AssertionResult.RateLimited rl -> "rl:" + rl.bucket();
};
assertThat(tag).isNotBlank();
}

AssertionResult.UnknownCredential uc1 =
new AssertionResult.UnknownCredential(CredentialId.of(new byte[] {1, 2}));
AssertionResult.UnknownCredential uc2 =
new AssertionResult.UnknownCredential(CredentialId.of(new byte[] {1, 2}));
assertThat(uc1).isEqualTo(uc2).hasSameHashCodeAs(uc2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,4 @@ void ceremonyConfigRejectsNonPositiveTtl() {
CounterRegressionPolicy.REJECT))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void counterRegressionPolicyValues() {
assertThat(CounterRegressionPolicy.values())
.containsExactly(CounterRegressionPolicy.REJECT, CounterRegressionPolicy.WARN);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,4 @@ void rejectsEmptyChallenge() {
new byte[0], ChallengeRecord.Purpose.REGISTRATION, null, Instant.now()))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void purposeEnumValues() {
assertThat(ChallengeRecord.Purpose.values())
.containsExactly(
ChallengeRecord.Purpose.REGISTRATION, ChallengeRecord.Purpose.AUTHENTICATION);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MIT
package com.codeheadsystems.pkauth.spi;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import com.codeheadsystems.pkauth.api.UserHandle;
Expand Down Expand Up @@ -32,19 +31,6 @@ private static OtpRepository.StoredOtp validOtp(int attempts, int maxAttempts) {
NOW.plusSeconds(60));
}

@Test
void storedOtpExposesAllFields() {
OtpRepository.StoredOtp otp = validOtp(1, 5);
assertThat(otp.otpId()).isEqualTo("otp-1");
assertThat(otp.userHandle()).isEqualTo(USER);
assertThat(otp.phoneE164()).isEqualTo("+15551234567");
assertThat(otp.hashedCode()).isEqualTo("hash");
assertThat(otp.attempts()).isEqualTo(1);
assertThat(otp.maxAttempts()).isEqualTo(5);
assertThat(otp.consumed()).isFalse();
assertThat(otp.expiresAt()).isEqualTo(NOW.plusSeconds(60));
}

@Test
void storedOtpRejectsNullRequiredFields() {
assertThatThrownBy(
Expand Down Expand Up @@ -79,18 +65,6 @@ void storedOtpRejectsNegativeAttemptsAndNonPositiveMax() {

// -- StoredBackupCode ---------------------------------------------------------------------

@Test
void storedBackupCodeExposesFieldsAndAllowsNullConsumedAt() {
BackupCodeRepository.StoredBackupCode code =
new BackupCodeRepository.StoredBackupCode("c-1", USER, "hash", false, NOW, null);
assertThat(code.codeId()).isEqualTo("c-1");
assertThat(code.userHandle()).isEqualTo(USER);
assertThat(code.hashedCode()).isEqualTo("hash");
assertThat(code.consumed()).isFalse();
assertThat(code.createdAt()).isEqualTo(NOW);
assertThat(code.consumedAt()).isNull();
}

@Test
void storedBackupCodeRejectsNullRequiredFields() {
assertThatThrownBy(
Expand Down
Loading
Loading