implement integration tests for person operations

This commit is contained in:
2025-04-19 22:32:27 +02:00
parent 07bd4d95a6
commit 58ef6ecbd7
16 changed files with 677 additions and 34 deletions

View File

@@ -7,6 +7,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/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/api/auth"
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/memgraph"
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/pkg/api"
@@ -38,8 +39,9 @@ func (srv *server) CreatePerson(c *gin.Context, params api.CreatePersonParams) {
qctx, qCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
defer qCancel()
convertedPerson := memgraph.StructToMap(person)
res, err := trs.Run(qctx, memgraph.CreatePersonCypherQuery, map[string]any{
"Person": *person,
"Person": convertedPerson,
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
@@ -54,17 +56,19 @@ func (srv *server) CreatePerson(c *gin.Context, params api.CreatePersonParams) {
return
}
personId, ok := singleRes.Get("id")
createdPerson, ok := singleRes.Get("person")
if !ok {
c.JSON(http.StatusInternalServerError, gin.H{"msg": "Person ID not found in response"})
c.JSON(http.StatusInternalServerError, gin.H{"msg": "Person not found in db response"})
return
}
personId := createdPerson.(dbtype.Node).Id //nolint:staticcheck // this is a difference in neo4j and memgraph
actx, acancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
defer acancel()
_, aErr := trs.Run(actx, memgraph.CreateAdminRelationshipCypherQuery, map[string]any{
"id2": personId.(int),
"id2": personId,
"id1": params.XUserID,
})
if aErr != nil {
@@ -80,10 +84,10 @@ func (srv *server) CreatePerson(c *gin.Context, params api.CreatePersonParams) {
return
}
c.JSON(http.StatusOK, singleRes.AsMap())
c.JSON(http.StatusOK, createdPerson)
}
func (srv *server) GetPersonById(c *gin.Context, id int, params api.GetPersonByIdParams) { //nolint:dupl // not worth abstracting more
func (srv *server) GetPersonById(c *gin.Context, id int, params api.GetPersonByIdParams) {
session := srv.createSessionWithTimeout(c.Request.Context())
defer closeSession(c.Request.Context(), srv.logger, session, srv.dbOpTimeout)
@@ -107,7 +111,7 @@ func (srv *server) GetPersonById(c *gin.Context, id int, params api.GetPersonByI
c.JSON(http.StatusOK, res)
}
func (srv *server) SoftDeletePerson(c *gin.Context, id int, params api.SoftDeletePersonParams) {
func (srv *server) SoftDeletePerson(c *gin.Context, id int, params api.SoftDeletePersonParams) { //nolint:dupl,lll // This just does not worth abstracting anymore
session := srv.createSessionWithTimeout(c.Request.Context())
defer closeSession(c.Request.Context(), srv.logger, session, srv.dbOpTimeout)
@@ -128,7 +132,7 @@ func (srv *server) SoftDeletePerson(c *gin.Context, id int, params api.SoftDelet
return
}
c.JSON(http.StatusOK, map[string]string{"description": "Person soft deleted"})
c.JSON(http.StatusOK, gin.H{"description": "Person soft deleted"})
}
func (srv *server) UpdatePerson(c *gin.Context, id int, params api.UpdatePersonParams) {
@@ -176,12 +180,14 @@ func (srv *server) HardDeletePerson(c *gin.Context, id int, params api.HardDelet
qctx, qCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
defer qCancel()
res, err := session.ExecuteWrite(qctx, memgraph.HardDeletePerson(qctx, id))
_, err := session.ExecuteWrite(qctx, memgraph.HardDeletePerson(qctx, id))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
return
}
c.JSON(http.StatusOK, res)
c.JSON(http.StatusOK, gin.H{
"description": "Person hard deleted",
})
}

View File

@@ -6,6 +6,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/vcscsvcscs/GenerationsHeritage/apps/db-adapter/internal/memgraph"
"github.com/vcscsvcscs/GenerationsHeritage/apps/db-adapter/pkg/api"
"go.uber.org/zap"
@@ -36,8 +37,9 @@ func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params ap
qctx, qCancel := context.WithTimeout(context.Background(), srv.dbOpTimeout)
defer qCancel()
convertedPerson := memgraph.StructToMap(requestBody.Person)
res, err := trs.Run(qctx, memgraph.CreatePersonCypherQuery, map[string]any{
"Person": requestBody.Person,
"Person": convertedPerson,
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
@@ -51,17 +53,19 @@ func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params ap
return
}
personID, ok := singleRes.Get("id")
createdPerson, ok := singleRes.Get("person")
if !ok {
c.JSON(http.StatusInternalServerError, gin.H{"msg": "Person ID not found in response"})
return
}
personID := createdPerson.(dbtype.Node).Id //nolint:staticcheck // this is a difference in neo4j and memgraph
actx, acancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
defer acancel()
_, aErr := trs.Run(actx, memgraph.CreateAdminRelationshipCypherQuery, map[string]any{
"id2": personID.(int),
"id2": personID,
"id1": params.XUserID,
})
if aErr != nil {
@@ -73,36 +77,38 @@ func (srv *server) CreatePersonAndRelationship(c *gin.Context, id int, params ap
relCtx, relCCancel := context.WithTimeout(c.Request.Context(), srv.dbOpTimeout)
defer relCCancel()
convertedRelationship := memgraph.StructToMap(requestBody.Relationship)
var relationShipResultRaw any
var relationshipError error
switch *requestBody.Type {
case api.CreatePersonAndRelationshipJSONBodyTypeChild:
relationShipResultRaw, relationshipError = trs.Run(relCtx, memgraph.CreateChildParentRelationshipCypherQuery, map[string]any{
"childId": personID.(int),
"childId": personID,
"parentId": id,
"childRelationship": requestBody.Relationship,
"parentRelationship": requestBody.Relationship,
"childRelationship": convertedRelationship,
"parentRelationship": convertedRelationship,
})
case api.CreatePersonAndRelationshipJSONBodyTypeParent:
relationShipResultRaw, relationshipError = trs.Run(relCtx, memgraph.CreateChildParentRelationshipCypherQuery, map[string]any{
"childId": id,
"parentId": personID.(int),
"childRelationship": requestBody.Relationship,
"parentRelationship": requestBody.Relationship,
"parentId": personID,
"childRelationship": convertedRelationship,
"parentRelationship": convertedRelationship,
})
case api.CreatePersonAndRelationshipJSONBodyTypeSibling:
relationShipResultRaw, relationshipError = trs.Run(relCtx, memgraph.CreateSiblingRelationshipCypherQuery, map[string]any{
"id1": id,
"id2": personID.(int),
"Relationship1": requestBody.Relationship,
"Relationship2": requestBody.Relationship,
"id2": personID,
"Relationship1": convertedRelationship,
"Relationship2": convertedRelationship,
})
case api.CreatePersonAndRelationshipJSONBodyTypeSpouse:
relationShipResultRaw, relationshipError = trs.Run(relCtx, memgraph.CreateSpouseRelationshipCypherQuery, map[string]any{
"id1": personID.(int),
"id1": personID,
"id2": id,
"Relationship1": requestBody.Relationship,
"Relationship2": requestBody.Relationship,
"Relationship1": convertedRelationship,
"Relationship2": convertedRelationship,
})
default:
c.JSON(http.StatusBadRequest, gin.H{"msg": "invalid relationship type"})

View File

@@ -41,7 +41,12 @@ func GetPersonById(ctx context.Context, id int) neo4j.ManagedTransactionWork {
return nil, err
}
return record.AsMap(), nil
person, ok := record.Get("person")
if !ok {
return nil, fmt.Errorf("person not found")
}
return person, nil
}
}
@@ -61,7 +66,12 @@ func UpdatePerson(ctx context.Context, id int, person *api.PersonProperties) neo
return nil, err
}
return record.AsMap(), nil
person, ok := record.Get("person")
if !ok {
return nil, fmt.Errorf("person not found")
}
return person, nil
}
}

View File

@@ -1,4 +1,5 @@
MATCH (n:Person {id: $id})
MATCH (n:Person)
WHERE id(n)=$id
SET n:DeletedPerson
REMOVE n:Person
RETURN labels(n) AS labels, n AS person

View File

@@ -71,6 +71,8 @@ func StructToMap(input any) map[string]any {
switch val.Kind() {
case reflect.Struct:
result[jsonKey] = StructToMap(val.Interface())
case reflect.Slice, reflect.Array:
result[jsonKey] = processSlice(val)
default:
result[jsonKey] = val.Interface()
}
@@ -104,3 +106,19 @@ func isPreservedType(v any) bool {
return false
}
}
func processSlice(val reflect.Value) []any {
slice := make([]any, val.Len())
for i := range val.Len() {
item := val.Index(i).Interface()
if isPreservedType(item) {
slice[i] = item
} else if reflect.ValueOf(item).Kind() == reflect.Struct {
slice[i] = StructToMap(item)
} else {
slice[i] = item
}
}
return slice
}