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
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package dev.openfga.sdk.api.client;

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

import dev.openfga.sdk.api.model.Assertion;
import java.util.List;
import org.junit.jupiter.api.Test;

class ClientAssertionTest {

@Test
void asAssertion_convertsAllFields() {
ClientAssertion client = new ClientAssertion()
.user("user:anne")
.relation("viewer")
._object("document:budget")
.expectation(true);

Assertion result = client.asAssertion();

assertThat(result.getTupleKey().getUser()).isEqualTo("user:anne");
assertThat(result.getTupleKey().getRelation()).isEqualTo("viewer");
assertThat(result.getTupleKey().getObject()).isEqualTo("document:budget");
assertThat(result.getExpectation()).isTrue();
}

@Test
void asAssertion_falseExpectation() {
ClientAssertion client = new ClientAssertion()
.user("user:bob")
.relation("editor")
._object("document:budget")
.expectation(false);

assertThat(client.asAssertion().getExpectation()).isFalse();
}

@Test
void asAssertions_withNullList_returnsEmpty() {
assertThat(ClientAssertion.asAssertions(null)).isEmpty();
}

@Test
void asAssertions_withEmptyList_returnsEmpty() {
assertThat(ClientAssertion.asAssertions(List.of())).isEmpty();
}

@Test
void asAssertions_convertsEachItem() {
List<ClientAssertion> input = List.of(
new ClientAssertion()
.user("user:anne")
.relation("viewer")
._object("doc:1")
.expectation(true),
new ClientAssertion()
.user("user:bob")
.relation("editor")
._object("doc:2")
.expectation(false));

List<Assertion> result = ClientAssertion.asAssertions(input);

assertThat(result).hasSize(2);
assertThat(result.get(0).getTupleKey().getUser()).isEqualTo("user:anne");
assertThat(result.get(0).getExpectation()).isTrue();
assertThat(result.get(1).getTupleKey().getUser()).isEqualTo("user:bob");
assertThat(result.get(1).getExpectation()).isFalse();
}
}
144 changes: 144 additions & 0 deletions src/test/java/dev/openfga/sdk/errors/FgaErrorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package dev.openfga.sdk.errors;

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

import java.net.http.HttpHeaders;
import java.util.Map;
import org.junit.jupiter.api.Test;

class FgaErrorTest {

private static FgaError error(int status) {
return new FgaError("original", status, HttpHeaders.of(Map.of(), (a, b) -> true), null);
}

// --- getMessage() format ---

@Test
void getMessage_withAllFields() {
FgaError error = error(400);
error.setOperationName("write");
error.setApiErrorMessage("type 'invalid_type' not found");
error.setApiErrorCode("validation_error");
error.setRequestId("abc-123");

assertThat(error.getMessage())
.isEqualTo("[write] HTTP 400 type 'invalid_type' not found (validation_error) [request-id: abc-123]");
}

@Test
void getMessage_withoutRequestId() {
FgaError error = error(400);
error.setOperationName("write");
error.setApiErrorMessage("type not found");
error.setApiErrorCode("validation_error");

assertThat(error.getMessage()).isEqualTo("[write] HTTP 400 type not found (validation_error)");
}

@Test
void getMessage_withoutOperationName() {
FgaError error = error(400);
error.setApiErrorMessage("type not found");
error.setApiErrorCode("validation_error");
error.setRequestId("abc-123");

assertThat(error.getMessage()).isEqualTo("HTTP 400 type not found (validation_error) [request-id: abc-123]");
}

@Test
void getMessage_withoutApiErrorCode() {
FgaError error = error(400);
error.setOperationName("write");
error.setApiErrorMessage("some message");
error.setRequestId("abc-123");

assertThat(error.getMessage()).isEqualTo("[write] HTTP 400 some message [request-id: abc-123]");
}

@Test
void getMessage_withOnlyStatusCode() {
FgaError error = new FgaError(null, 500, HttpHeaders.of(Map.of(), (a, b) -> true), null);

assertThat(error.getMessage()).isEqualTo("HTTP 500");
}

// --- helper method correctness ---

@Test
void isValidationError_trueWhenCodeMatches() {
FgaError error = error(400);
error.setApiErrorCode("validation_error");
assertThat(error.isValidationError()).isTrue();
}

@Test
void isValidationError_falseForOtherCode() {
FgaError error = error(400);
error.setApiErrorCode("auth_error");
assertThat(error.isValidationError()).isFalse();
}

@Test
void isUnknownError_trueWhenCodeIsUnknownError() {
FgaError error = error(400);
error.setApiErrorCode("unknown_error");
assertThat(error.isUnknownError()).isTrue();
}

@Test
void isRateLimitError_trueFor429() {
FgaError error = error(429);
assertThat(error.isRateLimitError()).isTrue();
}

@Test
void isRateLimitError_trueForCode() {
FgaError error = error(400);
error.setApiErrorCode("rate_limit_exceeded");
assertThat(error.isRateLimitError()).isTrue();
}

@Test
void isRetryable_trueFor429() {
assertThat(error(429).isRetryable()).isTrue();
}

@Test
void isRetryable_trueFor500() {
assertThat(error(500).isRetryable()).isTrue();
}

@Test
void isRetryable_falseFor400() {
assertThat(error(400).isRetryable()).isFalse();
}

@Test
void isRetryable_falseFor501() {
assertThat(error(501).isRetryable()).isFalse();
}

@Test
void isClientError_trueFor4xx() {
assertThat(error(400).isClientError()).isTrue();
assertThat(error(422).isClientError()).isTrue();
assertThat(error(429).isClientError()).isTrue();
}

@Test
void isClientError_falseFor5xx() {
assertThat(error(500).isClientError()).isFalse();
}

@Test
void isServerError_trueFor5xx() {
assertThat(error(500).isServerError()).isTrue();
assertThat(error(503).isServerError()).isTrue();
}

@Test
void isServerError_falseFor4xx() {
assertThat(error(400).isServerError()).isFalse();
}
}
37 changes: 37 additions & 0 deletions src/test/java/dev/openfga/sdk/errors/HttpStatusCodeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package dev.openfga.sdk.errors;

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

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

class HttpStatusCodeTest {

@ParameterizedTest
@CsvSource({"199, false", "200, true", "299, true", "300, false"})
void isSuccessful_boundaryValues(int status, boolean expected) {
assertThat(HttpStatusCode.isSuccessful(status)).isEqualTo(expected);
}

@ParameterizedTest
@CsvSource({"499, false", "500, true", "599, true", "600, false"})
void isServerError_boundaryValues(int status, boolean expected) {
assertThat(HttpStatusCode.isServerError(status)).isEqualTo(expected);
}

@ParameterizedTest
@CsvSource({
"400, false",
"429, true",
"500, true",
"501, false",
"502, true",
"503, true",
"504, true",
"599, true",
"600, false"
})
void isRetryable(int status, boolean expected) {
assertThat(HttpStatusCode.isRetryable(status)).isEqualTo(expected);
}
}
8 changes: 8 additions & 0 deletions src/test/java/dev/openfga/sdk/telemetry/MetricsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ void shouldGetCounter() {

// then
assertThat(longCounter).isNotNull();
assertThat(metrics.getCounter(counter, null, attributes)).isSameAs(longCounter);
}

@Test
Expand All @@ -70,6 +71,7 @@ void shouldGetHistogram() {

// then
assertThat(doubleHistogram).isNotNull();
assertThat(metrics.getHistogram(histogram, null, attributes)).isSameAs(doubleHistogram);
}

@Test
Expand All @@ -84,6 +86,8 @@ void shouldCredentialsRequest() {

// then
assertThat(longCounter).isNotNull();
assertThat(metrics.getCounter(Counters.CREDENTIALS_REQUEST, null, attributes))
.isSameAs(longCounter);
}

@Test
Expand All @@ -98,6 +102,8 @@ void shouldRequestDuration() {

// then
assertThat(doubleHistogram).isNotNull();
assertThat(metrics.getHistogram(Histograms.REQUEST_DURATION, null, attributes))
.isSameAs(doubleHistogram);
}

@Test
Expand All @@ -112,6 +118,8 @@ void shouldQueryDuration() {

// then
assertThat(doubleHistogram).isNotNull();
assertThat(metrics.getHistogram(Histograms.QUERY_DURATION, null, attributes))
.isSameAs(doubleHistogram);
}

@Test
Expand Down
3 changes: 0 additions & 3 deletions src/test/java/dev/openfga/sdk/util/RetryStrategyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ void calculateRetryDelay_withZeroRetryAfter_shouldEnforceMinimumDelay() {
Duration result = RetryStrategy.calculateRetryDelay(retryAfterDelay, retryCount, minimumRetryDelay);

// Then - Should enforce minimum delay to prevent hot-loop retries
// Current code will FAIL this test by returning Duration.ZERO
assertThat(result.toMillis()).isGreaterThanOrEqualTo(1); // At least 1ms to prevent hot-loops
}

Expand All @@ -135,7 +134,6 @@ void calculateRetryDelay_withNegativeRetryAfter_shouldEnforceMinimumDelay() {
Duration result = RetryStrategy.calculateRetryDelay(retryAfterDelay, retryCount, minimumRetryDelay);

// Then - Should enforce minimum delay to handle malformed server responses
// Current code will FAIL this test by returning negative duration
assertThat(result.toMillis()).isGreaterThanOrEqualTo(1); // At least 1ms for safety
}

Expand All @@ -150,7 +148,6 @@ void calculateRetryDelay_withVerySmallRetryAfter_shouldEnforceMinimumDelay() {
Duration result = RetryStrategy.calculateRetryDelay(retryAfterDelay, retryCount, minimumRetryDelay);

// Then - Should enforce reasonable minimum delay
// Current code will FAIL this test by returning tiny delay
assertThat(result.toMillis()).isGreaterThanOrEqualTo(1); // At least 1ms for system stability
}
}
Loading