/**
 * Go SDK for OpenFGA
 *
 * API version: 1.x
 * Website: https://openfga.dev
 * Documentation: https://openfga.dev/docs
 * Support: https://openfga.dev/community
 * License: [Apache-2.0](https://github.com/openfga/go-sdk/blob/main/LICENSE)
 *
 * NOTE: This file was auto generated by OpenAPI Generator (https://openapi-generator.tech). DO NOT EDIT.
 */

package openfga

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/jarcoal/httpmock"
	"github.com/openfga/go-sdk/credentials"
)

type TestDefinition struct {
	Name           string
	JsonResponse   string
	ResponseStatus int
	Method         string
	RequestPath    string
}

func TestOpenFgaApiConfiguration(t *testing.T) {
	t.Run("Providing no store id should not error", func(t *testing.T) {
		_, err := NewConfiguration(Configuration{
			ApiHost: "api.fga.example",
		})

		if err != nil {
			t.Fatalf("%v", err)
		}
	})

	t.Run("Providing no ApiHost should error", func(t *testing.T) {
		_, err := NewConfiguration(Configuration{})

		if err == nil {
			t.Fatalf("Expected an error when storeId is required but not provided")
		}
	})

	t.Run("ApiHost should be valid", func(t *testing.T) {
		_, err := NewConfiguration(Configuration{
			ApiHost: "https://api.fga.example",
		})

		if err == nil {
			t.Fatalf("Expected an error when ApiHost is invalid (scheme is part of the host)")
		}
	})

	t.Run("In ApiToken credential method, apiToken is required in the Credentials Config", func(t *testing.T) {
		_, err := NewConfiguration(Configuration{
			ApiHost: "https://api.fga.example",
			Credentials: &credentials.Credentials{
				Method: credentials.CredentialsMethodApiToken,
			},
		})

		if err == nil {
			t.Fatalf("Expected an error when apiToken is missing but the credential method is ApiToken")
		}
	})

	t.Run("should issue a successful network call when using ApiToken credential method", func(t *testing.T) {
		configuration, err := NewConfiguration(Configuration{
			ApiHost: "api.fga.example",
			Credentials: &credentials.Credentials{
				Method: credentials.CredentialsMethodApiToken,
				Config: &credentials.Config{
					ApiToken: "some-token",
				},
			},
		})
		if err != nil {
			t.Fatalf("%v", err)
		}

		apiClient := NewAPIClient(configuration)

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("GET", fmt.Sprintf("%s/stores/%s/authorization-models", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2"),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(200, ReadAuthorizationModelsResponse{AuthorizationModels: []AuthorizationModel{
					{
						Id:              "01GXSA8YR785C4FYS3C0RTG7B1",
						TypeDefinitions: []TypeDefinition{},
					},
					{
						Id:              "01GXSBM5PVYHCJNRNKXMB4QZTW",
						TypeDefinitions: []TypeDefinition{},
					},
				}})
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)

		if _, _, err = apiClient.OpenFgaApi.ReadAuthorizationModels(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").Execute(); err != nil {
			t.Fatalf("%v", err)
		}
	})

	t.Run("In ClientCredentials method, providing no client id, secret or issuer should error", func(t *testing.T) {
		_, err := NewConfiguration(Configuration{
			ApiHost: "https://api.fga.example",
			Credentials: &credentials.Credentials{
				Method: credentials.CredentialsMethodApiToken,
				Config: &credentials.Config{
					ClientCredentialsClientSecret: "some-secret",
				},
			},
		})

		if err == nil {
			t.Fatalf("Expected an error: client id is required")
		}

		_, err = NewConfiguration(Configuration{
			ApiHost: "https://api.fga.example",
			Credentials: &credentials.Credentials{
				Method: credentials.CredentialsMethodApiToken,
				Config: &credentials.Config{
					ClientCredentialsClientId:       "some-id",
					ClientCredentialsApiTokenIssuer: "some-issuer",
					ClientCredentialsApiAudience:    "some-audience",
				},
			},
		})

		if err == nil {
			t.Fatalf("Expected an error: client secret is required")
		}

		_, err = NewConfiguration(Configuration{
			ApiHost: "https://api.fga.example",
			Credentials: &credentials.Credentials{
				Method: credentials.CredentialsMethodApiToken,
				Config: &credentials.Config{
					ClientCredentialsClientId:     "some-id",
					ClientCredentialsClientSecret: "some-secret",
					ClientCredentialsApiAudience:  "some-audience",
				},
			},
		})

		if err == nil {
			t.Fatalf("Expected an error: api token issuer is required")
		}

		_, err = NewConfiguration(Configuration{
			ApiHost: "api.fga.example",
			Credentials: &credentials.Credentials{
				Method: credentials.CredentialsMethodClientCredentials,
				Config: &credentials.Config{
					ClientCredentialsClientId:       "some-id",
					ClientCredentialsClientSecret:   "some-secret",
					ClientCredentialsApiAudience:    "some-audience",
					ClientCredentialsApiTokenIssuer: "some-issuer.fga.example",
				},
			},
		})

		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
	})

	t.Run("NewCredentials should validate properly", func(t *testing.T) {
		// Passing valid credentials to NewCredentials should not error
		creds, err := credentials.NewCredentials(credentials.Credentials{
			Method: credentials.CredentialsMethodApiToken,
			Config: &credentials.Config{
				ApiToken: "some-token",
			},
		})

		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		if creds == nil {
			t.Fatalf("Expected creds to be non-nil")
		}

		if creds.Method != credentials.CredentialsMethodApiToken {
			t.Fatalf("Expected method to be %v, got %v", credentials.CredentialsMethodApiToken, creds.Method)
		}

		if creds.Config.ApiToken != "some-token" {
			t.Fatalf("Expected ApiToken to be %v, got %v", "some-token", creds.Config.ApiToken)
		}

		// Passing invalid credentials to NewCredentials should error
		_, err = credentials.NewCredentials(credentials.Credentials{
			Method: credentials.CredentialsMethodApiToken,
			Config: &credentials.Config{
				ClientCredentialsClientSecret: "some-secret",
			},
		})

		if err == nil {
			t.Fatalf("Expected validation error")
		}
	})

	clientCredentialsFirstRequestTest := func(t *testing.T, config Configuration, expectedTokenEndpoint string) {
		configuration, err := NewConfiguration(config)
		if err != nil {
			t.Fatalf("%v", err)
		}

		apiClient := NewAPIClient(configuration)

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("GET", fmt.Sprintf("%s/stores/%s/authorization-models", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2"),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(200, ReadAuthorizationModelsResponse{AuthorizationModels: []AuthorizationModel{
					{
						Id:              "01GXSA8YR785C4FYS3C0RTG7B1",
						TypeDefinitions: []TypeDefinition{},
					},
					{
						Id:              "01GXSBM5PVYHCJNRNKXMB4QZTW",
						TypeDefinitions: []TypeDefinition{},
					},
				}})
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)

		httpmock.RegisterResponder("POST", expectedTokenEndpoint,
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(200, struct {
					AccessToken string `json:"access_token"`
				}{AccessToken: "abcde"})
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)

		if _, _, err = apiClient.OpenFgaApi.ReadAuthorizationModels(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").Execute(); err != nil {
			t.Fatalf("%v", err)
		}

		info := httpmock.GetCallCountInfo()
		numCalls := info[fmt.Sprintf("POST %s", expectedTokenEndpoint)]
		if numCalls != 1 {
			t.Fatalf("Expected call to get access token to be made exactly once, saw: %d", numCalls)
		}
		numCalls = info[fmt.Sprintf("GET %s/stores/%s/authorization-models", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2")]
		if numCalls != 1 {
			t.Fatalf("Expected call to get authorization models to be made exactly once, saw: %d", numCalls)
		}
	}

	tokenIssuers := map[string]string{
		"issuer.fga.example":                            "https://issuer.fga.example/oauth/token",
		"https://issuer.fga.example":                    "https://issuer.fga.example/oauth/token",
		"https://issuer.fga.example/":                   "https://issuer.fga.example/oauth/token",
		"https://issuer.fga.example:8080":               "https://issuer.fga.example:8080/oauth/token",
		"https://issuer.fga.example:8080/":              "https://issuer.fga.example:8080/oauth/token",
		"issuer.fga.example/some_endpoint":              "https://issuer.fga.example/some_endpoint",
		"https://issuer.fga.example/some_endpoint":      "https://issuer.fga.example/some_endpoint",
		"https://issuer.fga.example:8080/some_endpoint": "https://issuer.fga.example:8080/some_endpoint",
	}

	for tokenIssuer, expectedTokenURL := range tokenIssuers {
		t.Run("should issue a network call to get the token at the first request if client id is provided", func(t *testing.T) {
			t.Run("with Auth0 configuration", func(t *testing.T) {
				clientCredentialsFirstRequestTest(t, Configuration{
					ApiUrl: "http://api.fga.example",
					Credentials: &credentials.Credentials{
						Method: credentials.CredentialsMethodClientCredentials,
						Config: &credentials.Config{
							ClientCredentialsClientId:       "some-id",
							ClientCredentialsClientSecret:   "some-secret",
							ClientCredentialsApiAudience:    "some-audience",
							ClientCredentialsApiTokenIssuer: tokenIssuer,
						},
					},
				}, expectedTokenURL)
			})
			t.Run("with OAuth2 configuration", func(t *testing.T) {
				clientCredentialsFirstRequestTest(t, Configuration{
					ApiUrl: "http://api.fga.example",
					Credentials: &credentials.Credentials{
						Method: credentials.CredentialsMethodClientCredentials,
						Config: &credentials.Config{
							ClientCredentialsClientId:       "some-id",
							ClientCredentialsClientSecret:   "some-secret",
							ClientCredentialsScopes:         "scope1 scope2",
							ClientCredentialsApiTokenIssuer: tokenIssuer,
						},
					},
				}, expectedTokenURL)
			})
		})
	}
	t.Run("should not issue a network call to get the token at the first request if the clientId is not provided", func(t *testing.T) {
		configuration, err := NewConfiguration(Configuration{
			ApiHost: "api.fga.example",
			Credentials: &credentials.Credentials{
				Method: credentials.CredentialsMethodNone,
				Config: &credentials.Config{ClientCredentialsApiTokenIssuer: "tokenissuer.api.example"},
			},
		})
		if err != nil {
			t.Fatalf("%v", err)
		}
		configuration.ApiHost = "api.fga.example"

		apiClient := NewAPIClient(configuration)

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("GET", fmt.Sprintf("%s/stores/%s/authorization-models", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2"),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(200, ReadAuthorizationModelsResponse{AuthorizationModels: []AuthorizationModel{
					{
						Id:              "01GXSA8YR785C4FYS3C0RTG7B1",
						TypeDefinitions: []TypeDefinition{},
					},
					{
						Id:              "01GXSBM5PVYHCJNRNKXMB4QZTW",
						TypeDefinitions: []TypeDefinition{},
					},
				}})
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)
		if _, _, err = apiClient.OpenFgaApi.ReadAuthorizationModels(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").Execute(); err != nil {
			t.Fatalf("%v", err)
		}

		info := httpmock.GetCallCountInfo()
		numCalls := info[fmt.Sprintf("POST https://%s/oauth/token", configuration.Credentials.Config.ClientCredentialsApiTokenIssuer)]
		if numCalls != 0 {
			t.Fatalf("Unexpected call to get access token made. Expected 0, saw: %d", numCalls)
		}
		numCalls = info[fmt.Sprintf("GET %s/stores/%s/authorization-models", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2")]
		if numCalls != 1 {
			t.Fatalf("Expected call to get authorization models to be made exactly once, saw: %d", numCalls)
		}
	})
}

func TestOpenFgaApi(t *testing.T) {
	configuration, err := NewConfiguration(Configuration{
		ApiHost: "api.fga.example",
	})
	if err != nil {
		t.Fatalf("%v", err)
	}

	apiClient := NewAPIClient(configuration)

	t.Run("ReadAuthorizationModels", func(t *testing.T) {
		test := TestDefinition{
			Name:           "ReadAuthorizationModels",
			JsonResponse:   `{"authorization_models":[{"id":"01GXSA8YR785C4FYS3C0RTG7B1","schema_version":"1.1","type_definitions":[]}]}`,
			ResponseStatus: 200,
			Method:         "GET",
			RequestPath:    "authorization-models",
		}

		var expectedResponse ReadAuthorizationModelsResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2", test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(test.ResponseStatus, expectedResponse)
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)

		got, response, err := apiClient.OpenFgaApi.ReadAuthorizationModels(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").Execute()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if response.StatusCode != test.ResponseStatus {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, response.StatusCode, test.ResponseStatus)
		}

		if len(got.AuthorizationModels) != 1 {
			t.Fatalf("%v", err)
		}

		if got.AuthorizationModels[0].Id != expectedResponse.AuthorizationModels[0].Id {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, got.AuthorizationModels[0].Id, expectedResponse.AuthorizationModels[0].Id)
		}
	})

	t.Run("WriteAuthorizationModel", func(t *testing.T) {
		test := TestDefinition{
			Name:           "WriteAuthorizationModel",
			JsonResponse:   `{"authorization_model_id":"01GXSA8YR785C4FYS3C0RTG7B1"}`,
			ResponseStatus: 200,
			Method:         "POST",
			RequestPath:    "authorization-models",
		}
		requestBody := WriteAuthorizationModelRequest{
			TypeDefinitions: []TypeDefinition{{
				Type: "github-repo",
				Relations: &map[string]Userset{
					"repo_writer": {
						This: &map[string]interface{}{},
					},
					"viewer": {Union: &Usersets{
						Child: []Userset{
							{This: &map[string]interface{}{}},
							{ComputedUserset: &ObjectRelation{
								Object:   PtrString(""),
								Relation: PtrString("repo_writer"),
							}},
						},
					}},
				},
			}},
		}

		var expectedResponse WriteAuthorizationModelResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2", test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(test.ResponseStatus, expectedResponse)
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)
		got, response, err := apiClient.OpenFgaApi.WriteAuthorizationModel(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").Body(requestBody).Execute()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if response.StatusCode != test.ResponseStatus {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, response.StatusCode, test.ResponseStatus)
		}

		_, err = got.MarshalJSON()
		if err != nil {
			t.Fatalf("%v", err)
		}
	})

	t.Run("ReadAuthorizationModel", func(t *testing.T) {
		test := TestDefinition{
			Name:           "ReadAuthorizationModel",
			JsonResponse:   `{"authorization_model":{"id":"01GXSA8YR785C4FYS3C0RTG7B1", "schema_version":"1.1", "type_definitions":[{"type":"github-repo", "relations":{"viewer":{"this":{}}}}]}}`,
			ResponseStatus: 200,
			Method:         "GET",
			RequestPath:    "authorization-models",
		}

		var expectedResponse ReadAuthorizationModelResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}
		modelId := expectedResponse.AuthorizationModel.Id

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s/%s", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2", test.RequestPath, modelId),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(test.ResponseStatus, expectedResponse)
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)
		got, response, err := apiClient.OpenFgaApi.ReadAuthorizationModel(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2", modelId).Execute()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if response.StatusCode != test.ResponseStatus {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, response.StatusCode, test.ResponseStatus)
		}

		responseJson, err := got.MarshalJSON()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if got.AuthorizationModel.Id != modelId {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, string(responseJson), test.JsonResponse)
		}
	})

	t.Run("Check", func(t *testing.T) {
		test := TestDefinition{
			Name:           "Check",
			JsonResponse:   `{"allowed":true, "resolution":""}`,
			ResponseStatus: 200,
			Method:         "POST",
			RequestPath:    "check",
		}
		requestBody := CheckRequest{
			TupleKey: CheckRequestTupleKey{
				User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
				Relation: "viewer",
				Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
			},
			AuthorizationModelId: PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
		}

		var expectedResponse CheckResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2", test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(test.ResponseStatus, expectedResponse)
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)
		got, response, err := apiClient.OpenFgaApi.Check(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").Body(requestBody).Execute()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if response.StatusCode != test.ResponseStatus {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, response.StatusCode, test.ResponseStatus)
		}

		responseJson, err := got.MarshalJSON()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if *got.Allowed != *expectedResponse.Allowed {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, string(responseJson), test.JsonResponse)
		}
	})

	t.Run("Write (Write Tuple)", func(t *testing.T) {
		test := TestDefinition{
			Name:           "Write",
			JsonResponse:   `{}`,
			ResponseStatus: 200,
			Method:         "POST",
			RequestPath:    "write",
		}
		requestBody := WriteRequest{
			Writes: &WriteRequestWrites{
				TupleKeys: []TupleKey{{
					User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
					Relation: "viewer",
					Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
				}},
			},
			AuthorizationModelId: PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
		}

		var expectedResponse map[string]interface{}
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2", test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(test.ResponseStatus, expectedResponse)
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)
		_, response, err := apiClient.OpenFgaApi.Write(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").Body(requestBody).Execute()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if response.StatusCode != test.ResponseStatus {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, response.StatusCode, test.ResponseStatus)
		}
	})

	t.Run("Write (Delete Tuple)", func(t *testing.T) {
		test := TestDefinition{
			Name:           "Write",
			JsonResponse:   `{}`,
			ResponseStatus: 200,
			Method:         "POST",
			RequestPath:    "write",
		}

		requestBody := WriteRequest{
			Deletes: &WriteRequestDeletes{
				TupleKeys: []TupleKeyWithoutCondition{{
					User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
					Relation: "viewer",
					Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
				}},
			},
			AuthorizationModelId: PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
		}

		var expectedResponse map[string]interface{}
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2", test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(test.ResponseStatus, expectedResponse)
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)
		_, response, err := apiClient.OpenFgaApi.Write(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").Body(requestBody).Execute()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if response.StatusCode != test.ResponseStatus {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, response.StatusCode, test.ResponseStatus)
		}
	})

	t.Run("Expand", func(t *testing.T) {
		test := TestDefinition{
			Name:           "Expand",
			JsonResponse:   `{"tree":{"root":{"name":"document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a#viewer","union":{"nodes":[{"name": "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a#viewer","leaf":{"users":{"users":["user:81684243-9356-4421-8fbf-a4f8d36aa31b"]}}}]}}}}`,
			ResponseStatus: 200,
			Method:         "POST",
			RequestPath:    "expand",
		}

		requestBody := ExpandRequest{
			TupleKey: ExpandRequestTupleKey{
				Relation: "viewer",
				Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
			},
			AuthorizationModelId: PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
		}

		var expectedResponse ExpandResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2", test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(test.ResponseStatus, expectedResponse)
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)
		got, response, err := apiClient.OpenFgaApi.Expand(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").Body(requestBody).Execute()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if response.StatusCode != test.ResponseStatus {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, response.StatusCode, test.ResponseStatus)
		}

		_, err = got.MarshalJSON()
		if err != nil {
			t.Fatalf("%v", err)
		}
	})

	t.Run("Read", func(t *testing.T) {
		test := TestDefinition{
			Name:           "Read",
			JsonResponse:   `{"tuples":[{"key":{"user":"user:81684243-9356-4421-8fbf-a4f8d36aa31b","relation":"viewer","object":"document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"},"timestamp": "2000-01-01T00:00:00Z"}]}`,
			ResponseStatus: 200,
			Method:         "POST",
			RequestPath:    "read",
		}

		requestBody := ReadRequest{
			TupleKey: &ReadRequestTupleKey{
				User:     PtrString("user:81684243-9356-4421-8fbf-a4f8d36aa31b"),
				Relation: PtrString("viewer"),
				Object:   PtrString("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"),
			},
		}

		var expectedResponse ReadResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2", test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(test.ResponseStatus, expectedResponse)
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)
		got, response, err := apiClient.OpenFgaApi.Read(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").Body(requestBody).Execute()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if response.StatusCode != test.ResponseStatus {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, response.StatusCode, test.ResponseStatus)
		}

		responseJson, err := got.MarshalJSON()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if len(got.Tuples) != len(expectedResponse.Tuples) {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, string(responseJson), test.JsonResponse)
		}
	})

	t.Run("ReadChanges", func(t *testing.T) {
		test := TestDefinition{
			Name:           "ReadChanges",
			JsonResponse:   `{"changes":[{"tuple_key":{"user":"user:81684243-9356-4421-8fbf-a4f8d36aa31b","relation":"viewer","object":"document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"},"operation":"TUPLE_OPERATION_WRITE","timestamp": "2000-01-01T00:00:00Z"}],"continuation_token":"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ=="}`,
			ResponseStatus: 200,
			Method:         "GET",
			RequestPath:    "changes",
		}

		var expectedResponse ReadChangesResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2", test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(test.ResponseStatus, expectedResponse)
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)
		startTime, err := time.Parse(time.RFC3339, "2022-01-01T00:00:00Z")
		got, response, err := apiClient.OpenFgaApi.ReadChanges(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").
			Type_("repo").
			PageSize(25).
			StartTime(startTime).
			ContinuationToken("eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==").
			Execute()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if response.StatusCode != test.ResponseStatus {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, response.StatusCode, test.ResponseStatus)
		}

		responseJson, err := got.MarshalJSON()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if len(got.Changes) != len(expectedResponse.Changes) {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, string(responseJson), test.JsonResponse)
		}
	})

	t.Run("ListObjects", func(t *testing.T) {
		test := TestDefinition{
			Name:           "ListObjects",
			JsonResponse:   `{"objects":["document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"]}`,
			ResponseStatus: 200,
			Method:         "POST",
			RequestPath:    "list-objects",
		}

		requestBody := ListObjectsRequest{
			AuthorizationModelId: PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
			User:                 "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
			Relation:             "can_read",
			Type:                 "document",
			ContextualTuples: &ContextualTupleKeys{
				TupleKeys: []TupleKey{{
					User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
					Relation: "editor",
					Object:   "folder:product",
				}, {
					User:     "folder:product",
					Relation: "parent",
					Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
				}},
			},
		}

		var expectedResponse ListObjectsResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2", test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(test.ResponseStatus, expectedResponse)
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)
		got, response, err := apiClient.OpenFgaApi.ListObjects(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").
			Body(requestBody).
			Execute()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if response.StatusCode != test.ResponseStatus {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, response.StatusCode, test.ResponseStatus)
		}

		responseJson, err := got.MarshalJSON()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if len(got.Objects) != len(expectedResponse.Objects) || (got.Objects)[0] != (expectedResponse.Objects)[0] {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, string(responseJson), test.JsonResponse)
		}
	})

	t.Run("ListUsers", func(t *testing.T) {
		test := TestDefinition{
			Name: "ListUsers",
			// A real API would not return all these for the filter provided, these are just for test purposes
			JsonResponse:   `{"users":[{"object":{"id":"81684243-9356-4421-8fbf-a4f8d36aa31b","type":"user"}},{"userset":{"id":"fga","relation":"member","type":"team"}},{"wildcard":{"type":"user"}}]}`,
			ResponseStatus: http.StatusOK,
			Method:         http.MethodPost,
			RequestPath:    "list-users",
		}

		requestBody := ListUsersRequest{
			AuthorizationModelId: PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
			Object: FgaObject{
				Type: "document",
				Id:   "roadmap",
			},
			Relation: "can_read",
			// API does not allow sending multiple filters, done here for testing purposes
			UserFilters: []UserTypeFilter{{
				Type: "user",
			}, {
				Type:     "team",
				Relation: PtrString("member"),
			}},
			ContextualTuples: &[]TupleKey{{
				User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
				Relation: "editor",
				Object:   "folder:product",
			}, {
				User:     "folder:product",
				Relation: "parent",
				Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
			}},
			Context: &map[string]interface{}{"ViewCount": 100},
		}

		var expectedResponse ListUsersResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2", test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				resp, err := httpmock.NewJsonResponse(test.ResponseStatus, expectedResponse)
				if err != nil {
					return httpmock.NewStringResponse(500, ""), nil
				}
				return resp, nil
			},
		)
		got, response, err := apiClient.OpenFgaApi.ListUsers(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").
			Body(requestBody).
			Execute()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if response.StatusCode != test.ResponseStatus {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, response.StatusCode, test.ResponseStatus)
		}

		_, err = got.MarshalJSON()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if len(got.Users) != len(expectedResponse.Users) {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, got.GetUsers(), expectedResponse.GetUsers())
		}

		if got.Users[0].GetObject().Type != expectedResponse.Users[0].GetObject().Type || got.Users[0].GetObject().Id != expectedResponse.Users[0].GetObject().Id {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v (%v)", test.Name, got.Users[0], expectedResponse.Users[0], "object: { type: \"user\", id: \"81684243-9356-4421-8fbf-a4f8d36aa31b\" }")
		}

		if got.Users[1].GetUserset().Type != expectedResponse.Users[1].GetUserset().Type || got.Users[1].GetUserset().Id != expectedResponse.Users[1].GetUserset().Id || got.Users[1].GetUserset().Relation != expectedResponse.Users[1].GetUserset().Relation {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v (%v)", test.Name, got.Users[1], expectedResponse.Users[1], "wildcard: { type: \"team\", id: \"fga\", relation: \"member\" }")
		}

		if got.Users[2].GetWildcard().Type != expectedResponse.Users[2].GetWildcard().Type {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v (%v)", test.Name, got.Users[2], expectedResponse.Users[2], "wildcard: { type: \"user\" }")
		}
	})

	t.Run("Check with 400 error", func(t *testing.T) {
		test := TestDefinition{
			Name:           "Check",
			JsonResponse:   `{"allowed":true, "resolution":""}`,
			ResponseStatus: 400,
			Method:         "POST",
			RequestPath:    "check",
		}
		requestBody := CheckRequest{
			TupleKey: CheckRequestTupleKey{
				User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
				Relation: "viewer",
				Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
			},
		}

		var expectedResponse CheckResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		storeId := "01GXSB9YR785C4FYS3C0RTG7B2"
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, storeId, test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				errObj := ErrorResponse{
					Code:    "validation_error",
					Message: "Foo",
				}
				return httpmock.NewJsonResponse(400, errObj)
			},
		)
		_, _, err := apiClient.OpenFgaApi.Check(context.Background(), storeId).Body(requestBody).Execute()
		if err == nil {
			t.Fatalf("Expected error with 400 request but there is none")
		}
		validationError, ok := err.(FgaApiValidationError)
		if !ok {
			t.Fatalf("Expected validation Error but type is incorrect %v", err)
		}
		// Do some basic validation of the error itself

		if validationError.StoreId() != storeId {
			t.Fatalf("Expected store id to be %s but actual %s", storeId, validationError.StoreId())
		}

		if validationError.EndpointCategory() != "Check" {
			t.Fatalf("Expected category to be Check but actual %s", validationError.EndpointCategory())
		}

		if validationError.RequestMethod() != "POST" {
			t.Fatalf("Expected category to be POST but actual %s", validationError.RequestMethod())
		}

		if validationError.ResponseStatusCode() != 400 {
			t.Fatalf("Expected status code to be 400 but actual %d", validationError.ResponseStatusCode())
		}

		if validationError.ResponseCode() != ERRORCODE_VALIDATION_ERROR {
			t.Fatalf("Expected response code to be ERRORCODE_VALIDATION_ERROR but actual %s", validationError.ResponseCode())
		}
	})

	t.Run("Check with 401 error", func(t *testing.T) {
		test := TestDefinition{
			Name:           "Check",
			JsonResponse:   `{"allowed":true, "resolution":""}`,
			ResponseStatus: 401,
			Method:         "POST",
			RequestPath:    "check",
		}
		requestBody := CheckRequest{
			TupleKey: CheckRequestTupleKey{
				User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
				Relation: "viewer",
				Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
			},
		}

		var expectedResponse CheckResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		storeId := "01GXSB9YR785C4FYS3C0RTG7B2"
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, storeId, test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				errObj := ErrorResponse{
					Code:    "auth_failure",
					Message: "Foo",
				}
				return httpmock.NewJsonResponse(401, errObj)
			},
		)
		_, _, err := apiClient.OpenFgaApi.Check(context.Background(), storeId).Body(requestBody).Execute()
		if err == nil {
			t.Fatalf("Expected error with 401 request but there is none")
		}
		authenticationError, ok := err.(FgaApiAuthenticationError)
		if !ok {
			t.Fatalf("Expected authentication Error but type is incorrect %v", err)
		}
		// Do some basic validation of the error itself

		if authenticationError.StoreId() != storeId {
			t.Fatalf("Expected store id to be %s but actual %s", storeId, authenticationError.StoreId())
		}

		if authenticationError.EndpointCategory() != "Check" {
			t.Fatalf("Expected category to be Check but actual %s", authenticationError.EndpointCategory())
		}

		if authenticationError.ResponseStatusCode() != 401 {
			t.Fatalf("Expected status code to be 401 but actual %d", authenticationError.ResponseStatusCode())
		}

	})

	t.Run("Check with 404 error", func(t *testing.T) {
		test := TestDefinition{
			Name:           "Check",
			JsonResponse:   `{"allowed":true, "resolution":""}`,
			ResponseStatus: 404,
			Method:         "POST",
			RequestPath:    "check",
		}
		requestBody := CheckRequest{
			TupleKey: CheckRequestTupleKey{
				User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
				Relation: "viewer",
				Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
			},
		}

		var expectedResponse CheckResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		storeId := "01GXSB9YR785C4FYS3C0RTG7B2"
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, storeId, test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				errObj := ErrorResponse{
					Code:    "undefined_endpoint",
					Message: "Foo",
				}
				return httpmock.NewJsonResponse(404, errObj)
			},
		)
		_, _, err := apiClient.OpenFgaApi.Check(context.Background(), storeId).Body(requestBody).Execute()
		if err == nil {
			t.Fatalf("Expected error with 404 request but there is none")
		}
		notFoundError, ok := err.(FgaApiNotFoundError)
		if !ok {
			t.Fatalf("Expected not found Error but type is incorrect %v", err)
		}
		// Do some basic validation of the error itself

		if notFoundError.StoreId() != storeId {
			t.Fatalf("Expected store id to be %s but actual %s", storeId, notFoundError.StoreId())
		}

		if notFoundError.EndpointCategory() != "Check" {
			t.Fatalf("Expected category to be Check but actual %s", notFoundError.EndpointCategory())
		}

		if notFoundError.RequestMethod() != "POST" {
			t.Fatalf("Expected category to be POST but actual %s", notFoundError.RequestMethod())
		}

		if notFoundError.ResponseStatusCode() != 404 {
			t.Fatalf("Expected status code to be 404 but actual %d", notFoundError.ResponseStatusCode())
		}

		if notFoundError.ResponseCode() != NOTFOUNDERRORCODE_UNDEFINED_ENDPOINT {
			t.Fatalf("Expected response code to be NOTFOUNDERRORCODE_UNDEFINED_ENDPOINT but actual %s", notFoundError.ResponseCode())
		}
	})

	t.Run("Check with 429 error", func(t *testing.T) {
		test := TestDefinition{
			Name:           "Check",
			JsonResponse:   `{"allowed":true, "resolution":""}`,
			ResponseStatus: 429,
			Method:         "POST",
			RequestPath:    "check",
		}
		requestBody := CheckRequest{
			TupleKey: CheckRequestTupleKey{
				User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
				Relation: "viewer",
				Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
			},
		}

		var expectedResponse CheckResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		storeId := "01GXSB9YR785C4FYS3C0RTG7B2"
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, storeId, test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				errObj := ErrorResponse{
					Code:    "rate_limit_exceeded",
					Message: "Foo",
				}
				return httpmock.NewJsonResponse(429, errObj)
			},
		)

		updatedConfiguration, err := NewConfiguration(Configuration{
			ApiHost: "api.fga.example",
			RetryParams: &RetryParams{
				MaxRetry:    3,
				MinWaitInMs: 5,
			},
		})
		if err != nil {
			t.Fatalf("%v", err)
		}

		updatedApiClient := NewAPIClient(updatedConfiguration)

		_, _, err = updatedApiClient.OpenFgaApi.Check(context.Background(), storeId).Body(requestBody).Execute()
		if err == nil {
			t.Fatalf("Expected error with 429 request but there is none")
		}
		rateLimitError, ok := err.(FgaApiRateLimitExceededError)
		if !ok {
			t.Fatalf("Expected rate limit exceeded Error but type is incorrect %v", err)
		}
		// Do some basic validation of the error itself

		if rateLimitError.StoreId() != storeId {
			t.Fatalf("Expected store id to be %s but actual %s", storeId, rateLimitError.StoreId())
		}

		if rateLimitError.EndpointCategory() != "Check" {
			t.Fatalf("Expected category to be Check but actual %s", rateLimitError.EndpointCategory())
		}

		if rateLimitError.ResponseStatusCode() != 429 {
			t.Fatalf("Expected status code to be 429 but actual %d", rateLimitError.ResponseStatusCode())
		}

	})

	t.Run("Check with initial 429 but eventually resolved", func(t *testing.T) {
		test := TestDefinition{
			Name:           "Check",
			JsonResponse:   `{"allowed":true, "resolution":""}`,
			ResponseStatus: 200,
			Method:         "POST",
			RequestPath:    "check",
		}
		requestBody := CheckRequest{
			TupleKey: CheckRequestTupleKey{
				User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
				Relation: "viewer",
				Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
			},
		}

		var expectedResponse CheckResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		firstMock := httpmock.NewStringResponder(429, "")
		secondMock, _ := httpmock.NewJsonResponder(200, expectedResponse)
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, "01GXSB9YR785C4FYS3C0RTG7B2", test.RequestPath),
			firstMock.Then(firstMock).Then(firstMock).Then(secondMock),
		)
		updatedConfiguration, err := NewConfiguration(Configuration{
			ApiHost: "api.fga.example",
			RetryParams: &RetryParams{
				MaxRetry:    2,
				MinWaitInMs: 5,
			},
		})
		if err != nil {
			t.Fatalf("%v", err)
		}

		updatedApiClient := NewAPIClient(updatedConfiguration)

		got, response, err := updatedApiClient.OpenFgaApi.Check(context.Background(), "01GXSB9YR785C4FYS3C0RTG7B2").Body(requestBody).Execute()

		if err != nil {
			t.Fatalf("%v", err)
		}

		if response.StatusCode != test.ResponseStatus {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, response.StatusCode, test.ResponseStatus)
		}

		responseJson, err := got.MarshalJSON()
		if err != nil {
			t.Fatalf("%v", err)
		}

		if *got.Allowed != *expectedResponse.Allowed {
			t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, string(responseJson), test.JsonResponse)
		}
	})

	t.Run("Check with 500 error", func(t *testing.T) {
		test := TestDefinition{
			Name:           "Check",
			JsonResponse:   `{"allowed":true, "resolution":""}`,
			ResponseStatus: 500,
			Method:         "POST",
			RequestPath:    "check",
		}
		requestBody := CheckRequest{
			TupleKey: CheckRequestTupleKey{
				User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
				Relation: "viewer",
				Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
			},
		}

		var expectedResponse CheckResponse
		if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil {
			t.Fatalf("%v", err)
		}

		storeId := "01GXSB9YR785C4FYS3C0RTG7B2"
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder(test.Method, fmt.Sprintf("%s/stores/%s/%s", configuration.ApiUrl, storeId, test.RequestPath),
			func(req *http.Request) (*http.Response, error) {
				errObj := ErrorResponse{
					Code:    "internal_error",
					Message: "Foo",
				}
				return httpmock.NewJsonResponse(500, errObj)
			},
		)
		_, _, err := apiClient.OpenFgaApi.Check(context.Background(), storeId).Body(requestBody).Execute()
		if err == nil {
			t.Fatalf("Expected error with 500 request but there is none")
		}
		internalError, ok := err.(FgaApiInternalError)
		if !ok {
			t.Fatalf("Expected internal Error but type is incorrect %v", err)
		}
		// Do some basic validation of the error itself

		if internalError.StoreId() != storeId {
			t.Fatalf("Expected store id to be %s but actual %s", storeId, internalError.StoreId())
		}

		if internalError.EndpointCategory() != "Check" {
			t.Fatalf("Expected category to be Check but actual %s", internalError.EndpointCategory())
		}

		if internalError.RequestMethod() != "POST" {
			t.Fatalf("Expected category to be POST but actual %s", internalError.RequestMethod())
		}

		if internalError.ResponseStatusCode() != 500 {
			t.Fatalf("Expected status code to be 500 but actual %d", internalError.ResponseStatusCode())
		}

		if internalError.ResponseCode() != INTERNALERRORCODE_INTERNAL_ERROR {
			t.Fatalf("Expected response code to be INTERNALERRORCODE_INTERNAL_ERROR but actual %s", internalError.ResponseCode())
		}
	})
}
