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
31 changes: 19 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,32 @@ This should bring up a grpc server on port **9000** and http server on port **80
```bash
grpcurl -plaintext localhost:9000 list

acre.v1.ResourcEncodingService
attributes.v1.AttributesService
attributes.AttributesService
grpc.reflection.v1.ServerReflection
grpc.reflection.v1alpha.ServerReflection

grpcurl -plaintext localhost:9000 list attributes.v1.AttributesService

attributes.v1.AttributesService.CreateAttribute
attributes.v1.AttributesService.DeleteAttribute
attributes.v1.AttributesService.GetAttribute
attributes.v1.AttributesService.ListAttributes
attributes.v1.AttributesService.UpdateAttribute

kasregistry.KeyAccessServerRegistryService
namespaces.NamespaceService
resourcemapping.ResourceMappingService
subjectmapping.SubjectMappingService

grpcurl -plaintext localhost:9000 list attributes.AttributesService

attributes.AttributesService.CreateAttribute
attributes.AttributesService.CreateAttributeValue
attributes.AttributesService.DeleteAttribute
attributes.AttributesService.DeleteAttributeValue
attributes.AttributesService.GetAttribute
attributes.AttributesService.GetAttributeValue
attributes.AttributesService.ListAttributeValues
attributes.AttributesService.ListAttributes
attributes.AttributesService.UpdateAttribute
attributes.AttributesService.UpdateAttributeValue
```

Create Attribute

```bash
grpcurl -plaintext -d @ localhost:9000 attributes.v1.AttributesService/CreateAttribute <<EOM
grpcurl -plaintext -d @ localhost:9000 attributes.v1.AttributesService/CreateAttribute <<EOM
{
"definition": {
"name": "relto",
Expand Down
312 changes: 312 additions & 0 deletions integration/attributes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
package integration

import (
"context"
"fmt"
"log/slog"
"testing"

"github.com/opentdf/opentdf-v2-poc/internal/db"
"github.com/opentdf/opentdf-v2-poc/sdk/attributes"
"github.com/opentdf/opentdf-v2-poc/sdk/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)

type AttributesSuite struct {
suite.Suite
schema string
f Fixtures
db DBInterface
ctx context.Context
}

var (
fixtureNamespaceId string
nonexistantAttrId string
)

func (s *AttributesSuite) SetupSuite() {
slog.Info("setting up db.Attributes test suite")
fixtureNamespaceId = fixtures.GetNamespaceKey("example.com").Id
nonexistantAttrId = "00000000-6789-4321-9876-123456765436"
s.ctx = context.Background()
s.schema = "test_opentdf_attribute_definitions"
s.db = NewDBInterface(s.schema)
s.f = NewFixture(s.db)
s.f.Provision()
}

func (s *AttributesSuite) TearDownSuite() {
slog.Info("tearing down db.Attributes test suite")
s.f.TearDown()
}

func getAttributeFixtures() []FixtureDataAttribute {
return []FixtureDataAttribute{
fixtures.GetAttributeKey("example.com/attr/attr1"),
fixtures.GetAttributeKey("example.com/attr/attr2"),
fixtures.GetAttributeKey("example.net/attr/attr1"),
fixtures.GetAttributeKey("example.net/attr/attr2"),
fixtures.GetAttributeKey("example.net/attr/attr3"),
fixtures.GetAttributeKey("example.org/attr/attr1"),
fixtures.GetAttributeKey("example.org/attr/attr2"),
fixtures.GetAttributeKey("example.org/attr/attr3"),
}
}

func (s *AttributesSuite) Test_CreateAttribute_NoMetadataSucceeds() {
attr := &attributes.AttributeCreateUpdate{
Name: "test__create_attribute_no_metadata",
NamespaceId: fixtureNamespaceId,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF,
}
createdAttr, err := s.db.Client.CreateAttribute(s.ctx, attr)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), createdAttr)
}

func (s *AttributesSuite) Test_CreateAttribute_WithMetadataSucceeds() {
attr := &attributes.AttributeCreateUpdate{
Name: "test__create_attribute_with_metadata",
NamespaceId: fixtureNamespaceId,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF,
Metadata: &common.MetadataMutable{
Labels: map[string]string{
"origin": "Some info about origin",
},
Description: "Attribute test description",
},
}
createdAttr, err := s.db.Client.CreateAttribute(s.ctx, attr)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), createdAttr)
}

func (s *AttributesSuite) Test_CreateAttribute_WithInvalidNamespaceFails() {
attr := &attributes.AttributeCreateUpdate{
Name: "test__create_attribute_invalid_namespace",
NamespaceId: "namespace_does_not_exist",
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF,
}
createdAttr, err := s.db.Client.CreateAttribute(s.ctx, attr)
assert.NotNil(s.T(), err)
assert.Nil(s.T(), createdAttr)
}

func (s *AttributesSuite) Test_CreateAttribute_WithNonUniqueNameConflictFails() {
attr := &attributes.AttributeCreateUpdate{
Name: fixtures.GetAttributeKey("example.com/attr/attr1").Name,
NamespaceId: fixtureNamespaceId,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF,
}
createdAttr, err := s.db.Client.CreateAttribute(s.ctx, attr)
assert.NotNil(s.T(), err)
assert.Nil(s.T(), createdAttr)
}

func (s *AttributesSuite) Test_CreateAttribute_WithEveryRuleSucceeds() {
otherNamespaceId := fixtures.GetNamespaceKey("example.net").Id
attr := &attributes.AttributeCreateUpdate{
Name: "test__create_attribute_with_any_of_rule_value",
NamespaceId: otherNamespaceId,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF,
}
createdAttr, err := s.db.Client.CreateAttribute(s.ctx, attr)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), createdAttr)

attr = &attributes.AttributeCreateUpdate{
Name: "test__create_attribute_with_all_of_rule_value",
NamespaceId: otherNamespaceId,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF,
}
createdAttr, err = s.db.Client.CreateAttribute(s.ctx, attr)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), createdAttr)

attr = &attributes.AttributeCreateUpdate{
Name: "test__create_attribute_with_unspecified_rule_value",
NamespaceId: otherNamespaceId,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED,
}
createdAttr, err = s.db.Client.CreateAttribute(s.ctx, attr)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), createdAttr)

attr = &attributes.AttributeCreateUpdate{
Name: "test__create_attribute_with_hierarchy_rule_value",
NamespaceId: otherNamespaceId,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY,
}
createdAttr, err = s.db.Client.CreateAttribute(s.ctx, attr)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), createdAttr)
}

func (s *AttributesSuite) Test_CreateAttribute_WithInvalidRuleFails() {
attr := &attributes.AttributeCreateUpdate{
Name: "test__create_attribute_with_invalid_rule",
NamespaceId: fixtureNamespaceId,
// fake an enum value index far beyond reason
Rule: attributes.AttributeRuleTypeEnum(100),
}
createdAttr, err := s.db.Client.CreateAttribute(s.ctx, attr)
assert.NotNil(s.T(), err)
assert.Nil(s.T(), createdAttr)
}

func (s *AttributesSuite) Test_GetAttribute() {
fixtures := getAttributeFixtures()

for _, f := range fixtures {
gotAttr, err := s.db.Client.GetAttribute(s.ctx, f.Id)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), gotAttr)
assert.Equal(s.T(), f.Id, gotAttr.Id)
assert.Equal(s.T(), f.Name, gotAttr.Name)
assert.Equal(s.T(), fmt.Sprintf("%s%s", db.AttributeRuleTypeEnumPrefix, f.Rule), gotAttr.Rule.Enum().String())
assert.Equal(s.T(), f.NamespaceId, gotAttr.Namespace.Id)
}
}

func (s *AttributesSuite) Test_GetAttribute_WithInvalidIdFails() {
// this uuid does not exist
gotAttr, err := s.db.Client.GetAttribute(s.ctx, nonexistantAttrId)
assert.NotNil(s.T(), err)
assert.Nil(s.T(), gotAttr)
// TODO: should be a not found error here
// assert.ErrorIs(s.T(), err, db.ErrNotFound)
}

func (s *AttributesSuite) Test_ListAttribute() {
fixtures := getAttributeFixtures()

list, err := s.db.Client.ListAllAttributes(s.ctx)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), list)

// all fixtures are listed
for _, f := range fixtures {
var found bool
for _, l := range list {
if f.Id == l.Id {
found = true
break
}
}
assert.True(s.T(), found)
}
}

func (s *AttributesSuite) Test_UpdateAttribute() {
attr := &attributes.AttributeCreateUpdate{
Name: "test__update_attribute",
NamespaceId: fixtureNamespaceId,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED,
}
createdAttr, err := s.db.Client.CreateAttribute(s.ctx, attr)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), createdAttr)

// change name and rule
update := &attributes.AttributeCreateUpdate{
Name: fmt.Sprintf("%s_updated_name", attr.Name),
NamespaceId: fixtureNamespaceId,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF,
}
resp, err := s.db.Client.UpdateAttribute(s.ctx, createdAttr.Id, update)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), resp)

updated, err := s.db.Client.GetAttribute(s.ctx, createdAttr.Id)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), updated)
assert.Equal(s.T(), update.Name, update.Name)
}

func (s *AttributesSuite) Test_UpdateAttribute_WithInvalidIdFails() {
update := &attributes.AttributeCreateUpdate{
Name: "test__update_attribute_invalid_id",
NamespaceId: fixtureNamespaceId,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED,
}
resp, err := s.db.Client.UpdateAttribute(s.ctx, nonexistantAttrId, update)
assert.NotNil(s.T(), err)
assert.Nil(s.T(), resp)
}

func (s *AttributesSuite) Test_UpdateAttribute_NamespaceIsImmutableOnUpdate() {
original := &attributes.AttributeCreateUpdate{
Name: "test__update_attribute_namespace_immutable",
NamespaceId: fixtures.GetNamespaceKey("example.com").Id,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED,
}
createdAttr, err := s.db.Client.CreateAttribute(s.ctx, original)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), createdAttr)

// should error on attempt to change namespace
update := &attributes.AttributeCreateUpdate{
Name: original.Name,
NamespaceId: fixtures.GetNamespaceKey("example.net").Id,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED,
}
resp, err := s.db.Client.UpdateAttribute(s.ctx, createdAttr.Id, update)
assert.NotNil(s.T(), err)
assert.Nil(s.T(), resp)

// validate namespace should not have been changed
updated, err := s.db.Client.GetAttribute(s.ctx, createdAttr.Id)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), updated)
assert.Equal(s.T(), original.NamespaceId, updated.Namespace.Id)
}

func (s *AttributesSuite) Test_UpdateAttributeWithSameNameAndNamespaceConflictFails() {
fixtureData := fixtures.GetAttributeKey("example.org/attr/attr3")
original := &attributes.AttributeCreateUpdate{
Name: "test__update_attribute_with_same_name_and_namespace",
NamespaceId: fixtureData.NamespaceId,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF,
}
createdAttr, err := s.db.Client.CreateAttribute(s.ctx, original)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), createdAttr)

conflict := &attributes.AttributeCreateUpdate{
Name: original.Name,
NamespaceId: original.NamespaceId,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF,
}
resp, err := s.db.Client.UpdateAttribute(s.ctx, fixtureData.Id, conflict)
assert.NotNil(s.T(), err)
assert.Nil(s.T(), resp)
}

func (s *AttributesSuite) Test_DeleteAttribute() {
attr := &attributes.AttributeCreateUpdate{
Name: "test__delete_attribute",
NamespaceId: fixtureNamespaceId,
Rule: attributes.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED,
}
createdAttr, err := s.db.Client.CreateAttribute(s.ctx, attr)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), createdAttr)

deleted, err := s.db.Client.DeleteAttribute(s.ctx, createdAttr.Id)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), deleted)

// should not exist anymore
resp, err := s.db.Client.GetAttribute(s.ctx, createdAttr.Id)
assert.NotNil(s.T(), err)
assert.Nil(s.T(), resp)
}

func TestAttributesSuite(t *testing.T) {
if testing.Short() {
t.Skip("skipping attributes integration tests")
}
suite.Run(t, new(AttributesSuite))
}
2 changes: 1 addition & 1 deletion integration/subject_mappings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (s *SubjectMappingsSuite) Test_GetSubjectMapping() {

func TestSubjectMappingSuite(t *testing.T) {
if testing.Short() {
t.Skip("skipping attributes integration tests")
t.Skip("skipping subject_mappings integration tests")
}
suite.Run(t, new(SubjectMappingsSuite))
}
Loading