implement relationship integration tests

This commit is contained in:
2025-04-24 15:08:34 +02:00
parent b5342a19ca
commit c8d68c5cc7
28 changed files with 323 additions and 83 deletions

View File

@@ -91,7 +91,7 @@ func TestCouldSeePersonsProfile(t *testing.T) {
err := CouldSeePersonsProfile(ctx, mockSession, 1, 3)
require.Error(t, err)
require.EqualError(t, err, "could not convert people to []api.PersonProperties")
require.EqualError(t, err, "could not convert people to []map[string]any: unexpected type: string")
mockSession.AssertExpectations(t)
})
}

View File

@@ -15,7 +15,6 @@ func FlattenFamilyTree(input any, result *FamilyTree) error {
root, ok := input.(map[string]any)
if !ok {
return fmt.Errorf("could not convert result to map[string]any")
}
var uniqueIds []int64

View File

@@ -13,7 +13,9 @@ import (
"go.uber.org/zap"
)
func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params api.CreatePersonAndRelationshipParams) {
func (srv *server) CreatePersonAndRelationship( //nolint:gocyclo,funlen // this is a complex function, but it is not too long
c *gin.Context, id int, params api.CreatePersonAndRelationshipParams,
) {
var requestBody api.CreatePersonAndRelationshipJSONBody
if err := c.ShouldBindJSON(&requestBody); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"msg": err.Error()})
@@ -87,6 +89,7 @@ func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params ap
convertedRelationship := memgraph.StructToMap(requestBody.Relationship)
var relationShipResultRaw neo4j.ResultWithContext
relationships := []any{}
var relationshipError error
switch *requestBody.Type {
case api.CreatePersonAndRelationshipJSONBodyTypeChild:
@@ -96,6 +99,19 @@ func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params ap
"childRelationship": convertedRelationship,
"parentRelationship": convertedRelationship,
})
sres, err := trs.Run(relCtx, memgraph.CreateSiblingRelationshipsBasedOnParentCypherQuery, map[string]any{ //nolint:govet,lll // this is intentional
"childId": personID,
"parentId": id,
})
if err == nil {
if srec, err := sres.Single(relCtx); err == nil {
if relationshipsRaw, ok := srec.Get("relationships"); ok { //nolint:govet // this is intentional
if rrelationships, ok := relationshipsRaw.([]any); ok {
relationships = append(relationships, rrelationships...)
}
}
}
}
case api.CreatePersonAndRelationshipJSONBodyTypeParent:
relationShipResultRaw, relationshipError = trs.Run(relCtx, memgraph.CreateChildParentRelationshipCypherQuery, map[string]any{
"childId": id,
@@ -133,13 +149,22 @@ func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params ap
return
}
relationships, ok := relationshipsSingle.Get("relationships")
rrelationships, ok := relationshipsSingle.Get("relationships")
if !ok {
c.JSON(http.StatusInternalServerError, gin.H{"msg": "no relationship was created"})
c.JSON(http.StatusInternalServerError, gin.H{"msg": "no relationships were found"})
return
}
rrrelationships, ok := rrelationships.([]any)
if !ok {
c.JSON(http.StatusInternalServerError, gin.H{"msg": "unknown relationships type were returned by database"})
return
}
relationships = append(relationships, rrrelationships...)
if err := trs.Commit(c.Request.Context()); err != nil {
srv.logger.Error("failed to commit transaction", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})

View File

@@ -10,6 +10,7 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
memgraphMock "github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/memgraph/mock"
@@ -76,7 +77,10 @@ func TestCreatePersonByGoogleIdAndInviteCode(t *testing.T) {
mockSession := new(memgraphMock.SessionWithContext)
mockDriver := new(memgraphMock.DriverWithContext)
mockDriver.On("NewSession", mock.Anything, mock.Anything).Return(mockSession)
mockSession.On("ExecuteWrite", mock.Anything, mock.Anything, mock.Anything).Return(map[string]any{"result": "success"}, nil)
mockSession.On("ExecuteWrite", mock.Anything, mock.Anything, mock.Anything).Return(
map[string]any{"person": dbtype.Node{Id: 3}},
nil,
)
mockSession.On("Close", mock.Anything).Return(nil)
srv := &server{
@@ -95,7 +99,7 @@ func TestCreatePersonByGoogleIdAndInviteCode(t *testing.T) {
srv.CreatePersonByGoogleIdAndInviteCode(c, "test-google-id")
assert.Equal(t, http.StatusOK, w.Code)
assert.Contains(t, w.Body.String(), "success")
assert.Contains(t, w.Body.String(), "Id")
})
t.Run("Bad request case", func(t *testing.T) {
@@ -124,7 +128,7 @@ func TestCreatePersonByGoogleId(t *testing.T) {
mockDriver := new(memgraphMock.DriverWithContext)
mockDriver.On("NewSession", mock.Anything, mock.Anything).Return(mockSession)
mockSession.On("ExecuteWrite", mock.Anything, mock.Anything, mock.Anything).Return(
map[string]any{"result": "success"}, nil,
map[string]any{"person": dbtype.Node{Id: 3}}, nil,
)
mockSession.On("Close", mock.Anything).Return(nil)
@@ -144,7 +148,7 @@ func TestCreatePersonByGoogleId(t *testing.T) {
srv.CreatePersonByGoogleId(c, "test-google-id")
assert.Equal(t, http.StatusOK, w.Code)
assert.Contains(t, w.Body.String(), "success")
assert.Contains(t, w.Body.String(), "Id")
})
t.Run("Bad request case", func(t *testing.T) {

View File

@@ -12,6 +12,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/memgraph"
@@ -27,8 +28,9 @@ func TestCreatePerson(t *testing.T) {
mockTransaction := new(memgraphMock.ExplicitTransaction)
mockResult := new(memgraphMock.Result)
mockResult.On("Single", mock.Anything).Return(&neo4j.Record{
Values: []any{1},
Keys: []string{"id"},
Values: []any{dbtype.Node{
Id: 1}},
Keys: []string{"person"},
}, nil)
mockDriver := new(memgraphMock.DriverWithContext)
mockDriver.On("NewSession", mock.Anything, mock.Anything).Return(mockSession)
@@ -57,7 +59,7 @@ func TestCreatePerson(t *testing.T) {
srv.CreatePerson(c, params)
assert.Equal(t, http.StatusOK, w.Code)
assert.Contains(t, w.Body.String(), "id")
assert.Contains(t, w.Body.String(), "Id")
})
t.Run("Bad request case", func(t *testing.T) {

View File

@@ -27,7 +27,7 @@ func (srv *server) CreateRelationship(c *gin.Context, params api.CreateRelations
defer aCancel()
if err := auth.CouldManagePersonUnknownAdmin(actx, session, *relationship.Id1, params.XUserID); err != nil {
if err := auth.CouldManagePersonUnknownAdmin(actx, session, *relationship.Id1, params.XUserID); err != nil {
if err := auth.CouldManagePersonUnknownAdmin(actx, session, *relationship.Id2, params.XUserID); err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have access to this person", err.Error())})
return
@@ -43,7 +43,7 @@ func (srv *server) CreateRelationship(c *gin.Context, params api.CreateRelations
case api.CreateRelationshipJSONBodyTypeChild:
relationShipResultRaw, relationshipError = session.ExecuteWrite(
qctx, memgraph.CreateChildParentRelationship(
qctx, *relationship.Id1, *relationship.Id2, *relationship.Relationship,
qctx, *relationship.Id2, *relationship.Id1, *relationship.Relationship,
),
)
case api.CreateRelationshipJSONBodyTypeParent:
@@ -91,7 +91,7 @@ func (srv *server) UpdateRelationship(c *gin.Context, id1, id2 int, params api.U
defer aCancel()
if err := auth.CouldManagePersonUnknownAdmin(actx, session, *relationship.Id1, params.XUserID); err != nil {
if err := auth.CouldManagePersonUnknownAdmin(actx, session, *relationship.Id1, params.XUserID); err != nil {
if err := auth.CouldManagePersonUnknownAdmin(actx, session, *relationship.Id2, params.XUserID); err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have access to this person", err.Error())})
return
@@ -117,8 +117,8 @@ func (srv *server) GetRelationship(c *gin.Context, id1, id2 int, params api.GetR
actx, aCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
defer aCancel()
if err := auth.CouldManagePersonUnknownAdmin(actx, session, id1, params.XUserID); err != nil {
if err := auth.CouldManagePersonUnknownAdmin(actx, session, id2, params.XUserID); err != nil {
if err := auth.CouldSeePersonsProfile(actx, session, id1, params.XUserID); err != nil {
if err := auth.CouldSeePersonsProfile(actx, session, id2, params.XUserID); err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"msg": fmt.Sprint("User does not have access to this person", err.Error())})
return

View File

@@ -29,7 +29,7 @@ func TestCreatePerson(t *testing.T) {
}
mockResult.On("Single", context.Background()).Return(mockRecord, nil)
mockTx.On("Run", context.Background(), CreatePersonCypherQuery, map[string]any{
"Person": api.PersonProperties{},
"Person": map[string]any{},
}).Return(mockResult, nil)
return mockTx
},
@@ -41,7 +41,7 @@ func TestCreatePerson(t *testing.T) {
mockTxSetup: func() *mock.Transaction {
mockTx := new(mock.Transaction)
mockTx.On("Run", context.Background(), CreatePersonCypherQuery, map[string]any{
"Person": api.PersonProperties{},
"Person": map[string]any{},
}).Return(nil, errors.New("run error"))
return mockTx
},
@@ -55,7 +55,7 @@ func TestCreatePerson(t *testing.T) {
mockResult := new(mock.Result)
mockResult.On("Single", context.Background()).Return(nil, errors.New("single error"))
mockTx.On("Run", context.Background(), CreatePersonCypherQuery, map[string]any{
"Person": api.PersonProperties{},
"Person": map[string]any{},
}).Return(mockResult, nil)
return mockTx
},
@@ -160,13 +160,13 @@ func TestUpdatePerson(t *testing.T) {
mockTx := new(mock.Transaction)
mockResult := new(mock.Result)
mockRecord := &neo4j.Record{
Values: []any{"updatedValue"},
Keys: []string{"updatedKey"},
Values: []any{map[string]any{"updatedKey": "updatedValue"}},
Keys: []string{"person"},
}
mockResult.On("Single", context.Background()).Return(mockRecord, nil)
mockTx.On("Run", context.Background(), UpdatePersonCypherQuery, map[string]any{
"id": 123,
"props": api.PersonProperties{},
"props": map[string]any{},
}).Return(mockResult, nil)
return mockTx
},
@@ -179,7 +179,7 @@ func TestUpdatePerson(t *testing.T) {
mockTx := new(mock.Transaction)
mockTx.On("Run", context.Background(), UpdatePersonCypherQuery, map[string]any{
"id": 123,
"props": api.PersonProperties{},
"props": map[string]any{},
}).Return(nil, errors.New("run error"))
return mockTx
},
@@ -194,7 +194,7 @@ func TestUpdatePerson(t *testing.T) {
mockResult.On("Single", context.Background()).Return(nil, errors.New("single error"))
mockTx.On("Run", context.Background(), UpdatePersonCypherQuery, map[string]any{
"id": 123,
"props": api.PersonProperties{},
"props": map[string]any{},
}).Return(mockResult, nil)
return mockTx
},

View File

@@ -1,6 +1,6 @@
MATCH (a:Person), (b:Person)
WHERE id(a) = $childId AND id(b) = $parentId AND $parentId != $childId
MERGE (a)-[r1:Child]->(b)-[r2:Parent]->(a)
MERGE (a)-[r1:Parent]->(b)-[r2:Child]->(a)
ON CREATE SET r1 = $childRelationship
ON CREATE SET r2 = $parentRelationship
RETURN collect(r1)+collect(r2) as relationships;

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype"
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/pkg/api"
)
@@ -108,16 +107,16 @@ func CreateChildParentRelationship(
return nil, err
}
relationships, rok := relationshipsRaw.([]dbtype.Relationship)
relationships, rok := relationshipsRaw.([]any)
if !rok {
return nil, err
}
siblingRecord, err := siblingResult.Single(ctx)
if err != nil {
if err == nil {
siblingRelationship, ok := siblingRecord.Get("relationships")
if ok {
siblings, ok := siblingRelationship.([]dbtype.Relationship)
siblings, ok := siblingRelationship.([]any)
if ok {
relationships = append(relationships, siblings...)
}
@@ -128,7 +127,7 @@ func CreateChildParentRelationship(
}
}
func CreateSiblingRelationship(
func CreateSiblingRelationship( //nolint:dupl // this is not worth fixing
ctx context.Context, siblingId1, siblingId2 int, relationship api.FamilyRelationship,
) neo4j.ManagedTransactionWork {
convertedRelationship := StructToMap(relationship)
@@ -157,7 +156,7 @@ func CreateSiblingRelationship(
}
}
func CreateSpouseRelationship(
func CreateSpouseRelationship( //nolint:dupl // this is not worth fixing
ctx context.Context, spouseId1, spouseId2 int, relationship api.FamilyRelationship,
) neo4j.ManagedTransactionWork {
convertedRelationship := StructToMap(relationship)

View File

@@ -10,7 +10,7 @@ import (
// StructToMap recursively converts a struct to a map using JSON tags.
// Nil pointers and unexported fields are excluded.
func StructToMap(input any) map[string]any {
func StructToMap(input any) map[string]any { //nolint:cyclop,gocyclo // this is a known issue with the neo4j-go-driver
result := make(map[string]any)
value := reflect.ValueOf(input)