package service_test import ( "testing" "github.com/oklog/ulid/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/openmeterio/openmeter/openmeter/taxcode" taxcodetestutils "github.com/openmeterio/openmeter/openmeter/taxcode/testutils" "github.com/openmeterio/openmeter/openmeter/testutils" "github.com/openmeterio/openmeter/pkg/models" ) func TestOrganizationDefaultTaxCodesService(t *testing.T) { env := taxcodetestutils.NewTestEnv(t) t.Cleanup(func() { env.Close(t) }) ns := testutils.NameGenerator.Generate().Key t.Run("Get", func(t *testing.T) { t.Run("ValidationError/EmptyNamespace", func(t *testing.T) { _, err := env.Service.GetOrganizationDefaultTaxCodes(t.Context(), taxcode.GetOrganizationDefaultTaxCodesInput{}) require.Error(t, err) assert.True(t, models.IsGenericValidationError(err)) }) t.Run("NotFound", func(t *testing.T) { _, err := env.Service.GetOrganizationDefaultTaxCodes(t.Context(), taxcode.GetOrganizationDefaultTaxCodesInput{ Namespace: testutils.NameGenerator.Generate().Key, }) require.Error(t, err) assert.True(t, taxcode.IsOrganizationDefaultTaxCodesNotFoundError(err)) }) }) t.Run("Upsert", func(t *testing.T) { t.Run("ValidationError/EmptyNamespace", func(t *testing.T) { tc := env.CreateTaxCode(t, ns) _, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), taxcode.UpsertOrganizationDefaultTaxCodesInput{ InvoicingTaxCodeID: tc.ID, CreditGrantTaxCodeID: tc.ID, }) require.Error(t, err) assert.True(t, models.IsGenericValidationError(err)) }) t.Run("ValidationError/EmptyInvoicingTaxCodeID", func(t *testing.T) { tc := env.CreateTaxCode(t, ns) _, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), taxcode.UpsertOrganizationDefaultTaxCodesInput{ Namespace: ns, CreditGrantTaxCodeID: tc.ID, }) require.Error(t, err) assert.True(t, models.IsGenericValidationError(err)) }) t.Run("ValidationError/EmptyCreditGrantTaxCodeID", func(t *testing.T) { tc := env.CreateTaxCode(t, ns) _, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), taxcode.UpsertOrganizationDefaultTaxCodesInput{ Namespace: ns, InvoicingTaxCodeID: tc.ID, }) require.Error(t, err) assert.True(t, models.IsGenericValidationError(err)) }) t.Run("NotFound/NonExistentInvoicingTaxCode", func(t *testing.T) { tc := env.CreateTaxCode(t, ns) _, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), taxcode.UpsertOrganizationDefaultTaxCodesInput{ Namespace: ns, InvoicingTaxCodeID: ulid.Make().String(), CreditGrantTaxCodeID: tc.ID, }) require.Error(t, err) assert.True(t, taxcode.IsTaxCodeNotFoundError(err)) }) t.Run("NotFound/NonExistentCreditGrantTaxCode", func(t *testing.T) { tc := env.CreateTaxCode(t, ns) _, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), taxcode.UpsertOrganizationDefaultTaxCodesInput{ Namespace: ns, InvoicingTaxCodeID: tc.ID, CreditGrantTaxCodeID: ulid.Make().String(), }) require.Error(t, err) assert.True(t, taxcode.IsTaxCodeNotFoundError(err)) }) t.Run("CrossNamespace/InvoicingTaxCodeFromOtherNamespace", func(t *testing.T) { otherNs := testutils.NameGenerator.Generate().Key otherTC := env.CreateTaxCode(t, otherNs) localTC := env.CreateTaxCode(t, ns) _, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), taxcode.UpsertOrganizationDefaultTaxCodesInput{ Namespace: ns, InvoicingTaxCodeID: otherTC.ID, CreditGrantTaxCodeID: localTC.ID, }) require.Error(t, err) assert.True(t, taxcode.IsTaxCodeNotFoundError(err), "tax code from another namespace must not resolve") }) t.Run("CrossNamespace/CreditGrantTaxCodeFromOtherNamespace", func(t *testing.T) { otherNs := testutils.NameGenerator.Generate().Key otherTC := env.CreateTaxCode(t, otherNs) localTC := env.CreateTaxCode(t, ns) _, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), taxcode.UpsertOrganizationDefaultTaxCodesInput{ Namespace: ns, InvoicingTaxCodeID: localTC.ID, CreditGrantTaxCodeID: otherTC.ID, }) require.Error(t, err) assert.True(t, taxcode.IsTaxCodeNotFoundError(err), "tax code from another namespace must not resolve") }) t.Run("Create", func(t *testing.T) { ns2 := testutils.NameGenerator.Generate().Key invoicing := env.CreateTaxCode(t, ns2) creditGrant := env.CreateTaxCode(t, ns2) result, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), taxcode.UpsertOrganizationDefaultTaxCodesInput{ Namespace: ns2, InvoicingTaxCodeID: invoicing.ID, CreditGrantTaxCodeID: creditGrant.ID, }) require.NoError(t, err) assert.Equal(t, ns2, result.Namespace) assert.Equal(t, invoicing.ID, result.InvoicingTaxCodeID) assert.Equal(t, creditGrant.ID, result.CreditGrantTaxCodeID) t.Run("Get", func(t *testing.T) { got, err := env.Service.GetOrganizationDefaultTaxCodes(t.Context(), taxcode.GetOrganizationDefaultTaxCodesInput{ Namespace: ns2, }) require.NoError(t, err) assert.Equal(t, result.ID, got.ID) assert.Equal(t, invoicing.ID, got.InvoicingTaxCodeID) assert.Equal(t, creditGrant.ID, got.CreditGrantTaxCodeID) }) t.Run("Update", func(t *testing.T) { newInvoicing := env.CreateTaxCode(t, ns2) updated, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), taxcode.UpsertOrganizationDefaultTaxCodesInput{ Namespace: ns2, InvoicingTaxCodeID: newInvoicing.ID, CreditGrantTaxCodeID: creditGrant.ID, }) require.NoError(t, err) assert.Equal(t, result.ID, updated.ID, "record ID must not change on update") assert.Equal(t, newInvoicing.ID, updated.InvoicingTaxCodeID) assert.Equal(t, creditGrant.ID, updated.CreditGrantTaxCodeID) }) }) t.Run("Idempotent", func(t *testing.T) { ns3 := testutils.NameGenerator.Generate().Key invoicing := env.CreateTaxCode(t, ns3) creditGrant := env.CreateTaxCode(t, ns3) input := taxcode.UpsertOrganizationDefaultTaxCodesInput{ Namespace: ns3, InvoicingTaxCodeID: invoicing.ID, CreditGrantTaxCodeID: creditGrant.ID, } first, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), input) require.NoError(t, err) second, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), input) require.NoError(t, err) assert.Equal(t, first.ID, second.ID, "row ID must be stable across identical upserts") assert.Equal(t, first.InvoicingTaxCodeID, second.InvoicingTaxCodeID) assert.Equal(t, first.CreditGrantTaxCodeID, second.CreditGrantTaxCodeID) assert.Equal(t, first.CreatedAt, second.CreatedAt, "created_at must not move on a no-op upsert") }) t.Run("SameTaxCodeForBothFields", func(t *testing.T) { ns4 := testutils.NameGenerator.Generate().Key tc := env.CreateTaxCode(t, ns4) result, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), taxcode.UpsertOrganizationDefaultTaxCodesInput{ Namespace: ns4, InvoicingTaxCodeID: tc.ID, CreditGrantTaxCodeID: tc.ID, }) require.NoError(t, err) assert.Equal(t, tc.ID, result.InvoicingTaxCodeID) assert.Equal(t, tc.ID, result.CreditGrantTaxCodeID) }) }) t.Run("Expand", func(t *testing.T) { nsExp := testutils.NameGenerator.Generate().Key invoicing := env.CreateTaxCode(t, nsExp) creditGrant := env.CreateTaxCode(t, nsExp) _, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), taxcode.UpsertOrganizationDefaultTaxCodesInput{ Namespace: nsExp, InvoicingTaxCodeID: invoicing.ID, CreditGrantTaxCodeID: creditGrant.ID, }) require.NoError(t, err) t.Run("NoExpand/TaxCodesAreNil", func(t *testing.T) { result, err := env.Service.GetOrganizationDefaultTaxCodes(t.Context(), taxcode.GetOrganizationDefaultTaxCodesInput{ Namespace: nsExp, }) require.NoError(t, err) assert.Nil(t, result.InvoicingTaxCode, "InvoicingTaxCode must be nil without expand") assert.Nil(t, result.CreditGrantTaxCode, "CreditGrantTaxCode must be nil without expand") assert.Equal(t, invoicing.ID, result.InvoicingTaxCodeID) assert.Equal(t, creditGrant.ID, result.CreditGrantTaxCodeID) }) t.Run("ExpandAll/TaxCodesArePopulated", func(t *testing.T) { result, err := env.Service.GetOrganizationDefaultTaxCodes(t.Context(), taxcode.GetOrganizationDefaultTaxCodesInput{ Namespace: nsExp, Expand: taxcode.OrganizationDefaultTaxCodesExpandAll, }) require.NoError(t, err) require.NotNil(t, result.InvoicingTaxCode) require.NotNil(t, result.CreditGrantTaxCode) assert.Equal(t, invoicing.ID, result.InvoicingTaxCode.ID) assert.Equal(t, creditGrant.ID, result.CreditGrantTaxCode.ID) }) t.Run("ExpandInvoicingOnly", func(t *testing.T) { result, err := env.Service.GetOrganizationDefaultTaxCodes(t.Context(), taxcode.GetOrganizationDefaultTaxCodesInput{ Namespace: nsExp, Expand: taxcode.OrganizationDefaultTaxCodesExpand{InvoicingTaxCode: true}, }) require.NoError(t, err) require.NotNil(t, result.InvoicingTaxCode) assert.Equal(t, invoicing.ID, result.InvoicingTaxCode.ID) assert.Nil(t, result.CreditGrantTaxCode, "CreditGrantTaxCode must be nil when not expanded") }) t.Run("ExpandCreditGrantOnly", func(t *testing.T) { result, err := env.Service.GetOrganizationDefaultTaxCodes(t.Context(), taxcode.GetOrganizationDefaultTaxCodesInput{ Namespace: nsExp, Expand: taxcode.OrganizationDefaultTaxCodesExpand{CreditGrantTaxCode: true}, }) require.NoError(t, err) assert.Nil(t, result.InvoicingTaxCode, "InvoicingTaxCode must be nil when not expanded") require.NotNil(t, result.CreditGrantTaxCode) assert.Equal(t, creditGrant.ID, result.CreditGrantTaxCode.ID) }) t.Run("UpsertWithExpand/ResponseContainsTaxCodes", func(t *testing.T) { nsExp2 := testutils.NameGenerator.Generate().Key inv := env.CreateTaxCode(t, nsExp2) cg := env.CreateTaxCode(t, nsExp2) result, err := env.Service.UpsertOrganizationDefaultTaxCodes(t.Context(), taxcode.UpsertOrganizationDefaultTaxCodesInput{ Namespace: nsExp2, InvoicingTaxCodeID: inv.ID, CreditGrantTaxCodeID: cg.ID, Expand: taxcode.OrganizationDefaultTaxCodesExpandAll, }) require.NoError(t, err) require.NotNil(t, result.InvoicingTaxCode) require.NotNil(t, result.CreditGrantTaxCode) assert.Equal(t, inv.ID, result.InvoicingTaxCode.ID) assert.Equal(t, cg.ID, result.CreditGrantTaxCode.ID) }) }) }