Compare commits

..

No commits in common. "75bbbb4408d9fc741ba3c7f8d8dcb0a3ee12f981" and "78e17cf1006063e53ef47ebc1c64bcaaff7d3466" have entirely different histories.

19 changed files with 439 additions and 82 deletions

View File

@ -8,18 +8,7 @@ import (
"go.opentelemetry.io/otel/trace"
)
// HandleError logs the provided error, records it in the given trace span,
// sets the span status to error, and returns the error.
//
// Parameters:
// - ctx: context.Context, the context in which the error occurred.
// - span: trace.Span, the trace span where the error will be recorded.
// - logger: *log.Entry, a log entry used to log the error message.
// - error: error, the error to be handled.
//
// Returns:
// - error: The same error that was passed in.
func HandleError(_ context.Context, span trace.Span, logger *log.Entry, error error) error {
func HandleError(ctx context.Context, span trace.Span, logger *log.Entry, error error) error {
logger.Error(error)
span.RecordError(error)
span.SetStatus(codes.Error, error.Error())

11
internal/utils/slices.go Normal file
View File

@ -0,0 +1,11 @@
package utils
func GetOrDefault(data map[string]any, key string, defaultVal any) any {
val, ok := data[key]
if !ok {
return defaultVal
}
return val
}

View File

@ -0,0 +1,60 @@
package utils
import (
"reflect"
"testing"
)
func TestGetOrDefault(t *testing.T) {
type args struct {
data map[string]any
key string
defaultVal any
}
tests := []struct {
name string
args args
want any
}{
{
name: "Test 1: Nil map",
args: args{
data: nil,
key: "key1",
defaultVal: "default",
},
want: "default",
},
{
name: "Test 2: Existing key",
args: args{
data: map[string]interface{}{
"key1": "value1",
"key2": "value2",
},
key: "key1",
defaultVal: "default",
},
want: "value1",
},
{
name: "Test 3: Non-existing key",
args: args{
data: map[string]interface{}{
"key1": "value1",
"key2": "value2",
},
key: "key3",
defaultVal: "default",
},
want: "default",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GetOrDefault(tt.args.data, tt.args.key, tt.args.defaultVal); !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetOrDefault() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -7,7 +7,6 @@ import (
"go.opentelemetry.io/otel/trace"
)
// SetupTracing initializes a new trace span and logger for the given context and tracer.
func SetupTracing(ctx context.Context, tracer trace.Tracer, tracerName string) (context.Context, trace.Span, *log.Entry) {
ctx, span := tracer.Start(ctx, tracerName)
localLogger := log.WithContext(ctx)
@ -15,7 +14,6 @@ func SetupTracing(ctx context.Context, tracer trace.Tracer, tracerName string) (
return ctx, span, localLogger
}
// HandleEvent logs the provided event name and adds it to the given trace span.
func HandleEvent(span trace.Span, logger *log.Entry, eventName string) {
logger.Debug(eventName)
span.AddEvent(eventName)

View File

@ -28,16 +28,9 @@ var (
logger = log.New()
)
// Connect to the Database
func Connect(ctx context.Context, config models.DatabaseConfig) error {
setupTelemetry()
// Setup open telemetry
tracer = otel.Tracer(tracingName)
hook := otellogrus.NewHook(tracingName)
logger.AddHook(hook)
// Debug enabled?
if config.Debug {
log.SetLevel(log.DebugLevel)
}
@ -82,7 +75,6 @@ func Connect(ctx context.Context, config models.DatabaseConfig) error {
return nil
}
// migrateDatabase handels the migration of ann SQL files in the migrations subfolder
func migrateDatabase(ctx context.Context, dbPool *gorm.DB, config models.DatabaseConfig) error {
ctx, span, localLogger := utils.SetupTracing(ctx, tracer, "migrateDatabase")
defer span.End()
@ -122,7 +114,13 @@ func migrateDatabase(ctx context.Context, dbPool *gorm.DB, config models.Databas
return nil
}
// GetGorm returns a ready to use gorm.DB client
func setupTelemetry() {
tracer = otel.Tracer(tracingName)
hook := otellogrus.NewHook(tracingName)
logger.AddHook(hook)
}
func GetGorm(ctx context.Context) (*gorm.DB, error) {
ctx, span, localLogger := utils.SetupTracing(ctx, tracer, "GetGorm")
defer span.End()

View File

@ -2,26 +2,79 @@ package error
import "testing"
func TestDatabase_Error(t *testing.T) {
type fields struct {
Reason string
}
func TestEntityAlreadyExists_Error(t *testing.T) {
tests := []struct {
name string
fields fields
want string
}{
{
name: "Test 1: Reason",
fields: fields{Reason: "TEST ERROR"},
want: "Database error: TEST ERROR",
name: "Test : Valid error String",
want: "EntityAlreadyExists error",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := Database{
Reason: tt.fields.Reason,
}
e := &EntityAlreadyExists{}
if got := e.Error(); got != tt.want {
t.Errorf("Error() = %v, want %v", got, tt.want)
}
})
}
}
func TestNoDataFound_Error(t *testing.T) {
tests := []struct {
name string
want string
}{
{
name: "Test : Valid error String",
want: "NoDataFound error",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := &NoDataFound{}
if got := e.Error(); got != tt.want {
t.Errorf("Error() = %v, want %v", got, tt.want)
}
})
}
}
func TestNoDataWritten_Error(t *testing.T) {
tests := []struct {
name string
want string
}{
{
name: "Test : Valid error String",
want: "NoDataWritten error",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := &NoDataWritten{}
if got := e.Error(); got != tt.want {
t.Errorf("Error() = %v, want %v", got, tt.want)
}
})
}
}
func TestNoRelationCreated_Error(t *testing.T) {
tests := []struct {
name string
want string
}{
{
name: "Test : Valid error String",
want: "relationship creation error",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := &NoRelationCreated{}
if got := e.Error(); got != tt.want {
t.Errorf("Error() = %v, want %v", got, tt.want)
}

View File

@ -3,6 +3,9 @@ package error
import "fmt"
const (
UserIDIsEmpty = "postID cannot be empty"
UserIDToShort = "postID needs to be 25 characters long"
SourceListIsEmpty = "sourceList cannot be empty"
SourceIDIsEmpty = "SourceID cannot be empty"
SourceIDToShort = "sourceID needs to be 25 characters long"
@ -12,8 +15,10 @@ const (
UserSourceIDToShort = "userSourceID needs to be 25 characters long"
TagListIsEmpty = "tagList cannot be empty"
TagNameIsEmpty = "tagName cannot be empty"
TagAliasListIsEmpty = "tagAliasList cannot be empty"
TagAliasNameIsEmpty = "tagAliasName cannot be empty"
TagGroupListIsEmpty = "tagGroupList cannot be empty"
TagGroupNameIsEmpty = "tagGroupName cannot be empty"

5
pkg/models/api.go Normal file
View File

@ -0,0 +1,5 @@
package models
type FavoriteList struct {
Posts []Post `json:"posts,omitempty"`
}

View File

@ -13,8 +13,8 @@ type ID interface {
type BaseModel[T ID] struct {
ID T `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
}

View File

@ -4,7 +4,7 @@ package models
type Post struct {
BaseModel[PostID]
Rating Rating `json:"rating" gorm:"type:enum('safe','questionable','explicit')"`
Tags []Tag `json:"tags,omitempty" gorm:"many2many:post_tags;"`
Tags []Tag `json:"-" gorm:"many2many:post_tags;"`
Favorites []UserFavorite `json:"-" gorm:"foreignKey:PostID"`
References []PostReference `json:"references" gorm:"foreignKey:PostID"`
}

View File

@ -3,9 +3,42 @@ package models
import "testing"
func TestPostReference_TableName(t *testing.T) {
postReference := PostReference{}
expectedTableName := "PostReference"
if tableName := postReference.TableName(); tableName != expectedTableName {
t.Fatalf("expected %s, but got %s", expectedTableName, tableName)
type fields struct {
PostID string
SourceID string
URL string
SourcePostID string
FullFileURL string
PreviewFileURL string
SampleFileURL string
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "Test 1: PostReference",
fields: fields{},
want: "PostReference",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
po := PostReference{
PostID: tt.fields.PostID,
SourceID: tt.fields.SourceID,
URL: tt.fields.URL,
PostReferenceConfig: PostReferenceConfig{
SourcePostID: tt.fields.SourcePostID,
FullFileURL: tt.fields.FullFileURL,
PreviewFileURL: tt.fields.PreviewFileURL,
SampleFileURL: tt.fields.SampleFileURL,
},
}
if got := po.TableName(); got != tt.want {
t.Errorf("TableName() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -3,9 +3,36 @@ package models
import "testing"
func TestPost_TableName(t *testing.T) {
post := Post{}
expectedTableName := "Post"
if tableName := post.TableName(); tableName != expectedTableName {
t.Fatalf("expected %s, but got %s", expectedTableName, tableName)
type fields struct {
BaseModel BaseModel[PostID]
Rating Rating
Tags []Tag
Favorites []UserFavorite
References []PostReference
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "Test 1: Is name Post",
fields: fields{},
want: "Post",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
po := Post{
BaseModel: tt.fields.BaseModel,
Rating: tt.fields.Rating,
Tags: tt.fields.Tags,
Favorites: tt.fields.Favorites,
References: tt.fields.References,
}
if got := po.TableName(); got != tt.want {
t.Errorf("TableName() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -3,9 +3,38 @@ package models
import "testing"
func TestSource_TableName(t *testing.T) {
source := Source{}
expectedTableName := "Source"
if tableName := source.TableName(); tableName != expectedTableName {
t.Fatalf("expected %s, but got %s", expectedTableName, tableName)
type fields struct {
BaseModel BaseModel[SourceID]
DisplayName string
Domain string
Icon string
UserSources []UserSource
References []PostReference
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "Test 1: Is name Source",
fields: fields{},
want: "Source",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
so := Source{
BaseModel: tt.fields.BaseModel,
DisplayName: tt.fields.DisplayName,
Domain: tt.fields.Domain,
Icon: tt.fields.Icon,
UserSources: tt.fields.UserSources,
References: tt.fields.References,
}
if got := so.TableName(); got != tt.want {
t.Errorf("TableName() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -4,9 +4,9 @@ package models
type Tag struct {
Name TagName `json:"name" gorm:"primaryKey"`
Type TagType `json:"type" gorm:"column:tag_type"`
Aliases []TagAlias `json:"aliases,omitempty" gorm:"foreignKey:TagID"`
Groups []TagGroup `json:"groups,omitempty" gorm:"foreignKey:TagID"`
Posts []Post `json:"posts,omitempty" gorm:"many2many:post_tags;"`
Aliases []TagAlias `json:"aliases" gorm:"foreignKey:TagID"`
Groups []TagGroup `json:"groups" gorm:"foreignKey:TagID"`
Posts []Post `json:"posts" gorm:"many2many:post_tags;"`
}
func (Tag) TableName() string {

View File

@ -3,25 +3,94 @@ package models
import "testing"
func TestTagAlias_TableName(t *testing.T) {
tagAlias := TagAlias{}
expectedTableName := "TagAlias"
if tableName := tagAlias.TableName(); tableName != expectedTableName {
t.Fatalf("expected %s, but got %s", expectedTableName, tableName)
type fields struct {
Name string
TagID string
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "Test 1: Is Name TagAlias",
fields: fields{},
want: "TagAlias",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ta := TagAlias{
Name: tt.fields.Name,
TagID: tt.fields.TagID,
}
if got := ta.TableName(); got != tt.want {
t.Errorf("TableName() = %v, want %v", got, tt.want)
}
})
}
}
func TestTagGroup_TableName(t *testing.T) {
tagGroup := TagGroup{}
expectedTableName := "TagGroup"
if tableName := tagGroup.TableName(); tableName != expectedTableName {
t.Fatalf("expected %s, but got %s", expectedTableName, tableName)
type fields struct {
Name string
TagID string
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "Test 1: Is name TagGroup",
fields: fields{},
want: "TagGroup",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ta := TagGroup{
Name: tt.fields.Name,
TagID: tt.fields.TagID,
}
if got := ta.TableName(); got != tt.want {
t.Errorf("TableName() = %v, want %v", got, tt.want)
}
})
}
}
func TestTags_TableName(t *testing.T) {
tag := Tag{}
expectedTableName := "Tag"
if tableName := tag.TableName(); tableName != expectedTableName {
t.Fatalf("expected %s, but got %s", expectedTableName, tableName)
func TestTag_TableName(t *testing.T) {
type fields struct {
Name string
Type TagType
Aliases []TagAlias
Groups []TagGroup
Posts []Post
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "Test 1: Is name Tag",
fields: fields{},
want: "Tag",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ta := Tag{
Name: tt.fields.Name,
Type: tt.fields.Type,
Aliases: tt.fields.Aliases,
Groups: tt.fields.Groups,
Posts: tt.fields.Posts,
}
if got := ta.TableName(); got != tt.want {
t.Errorf("TableName() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -1,11 +1,37 @@
package models
import "testing"
import (
"testing"
"time"
)
func TestUserFavorite_TableName(t *testing.T) {
userFavorite := UserFavorite{}
expectedTableName := "UserFavorites"
if tableName := userFavorite.TableName(); tableName != expectedTableName {
t.Fatalf("expected %s, but got %s", expectedTableName, tableName)
type fields struct {
UserID string
PostID string
CreatedAt time.Time
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "Test 1: Is name UserFavorite",
fields: fields{},
want: "UserFavorite",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
us := UserFavorite{
UserID: tt.fields.UserID,
PostID: tt.fields.PostID,
CreatedAt: tt.fields.CreatedAt,
}
if got := us.TableName(); got != tt.want {
t.Errorf("TableName() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -13,7 +13,7 @@ type UserSource struct {
AccountID string `json:"account_id"`
LastScrapeTime time.Time `json:"last_scrape_time"`
AccountValidate bool `json:"account_validate"`
AccountValidationKey string `json:"account_validation_key"`
AccountValidationKey string `json:"-"`
}
func (UserSource) TableName() string {

View File

@ -3,9 +3,40 @@ package models
import "testing"
func TestUserSource_TableName(t *testing.T) {
userSource := UserSource{}
expectedTableName := "UserSource"
if tableName := userSource.TableName(); tableName != expectedTableName {
t.Fatalf("expected %s, but got %s", expectedTableName, tableName)
type fields struct {
User User
UserID UserID
Source Source
SourceID SourceID
ScrapeTimeInterval string
AccountUsername string
AccountID string
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "Test 1: Is name UserSource",
fields: fields{},
want: "UserSource",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
us := UserSource{
User: tt.fields.User,
UserID: tt.fields.UserID,
Source: tt.fields.Source,
SourceID: tt.fields.SourceID,
ScrapeTimeInterval: tt.fields.ScrapeTimeInterval,
AccountUsername: tt.fields.AccountUsername,
AccountID: tt.fields.AccountID,
}
if got := us.TableName(); got != tt.want {
t.Errorf("TableName() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -3,9 +3,32 @@ package models
import "testing"
func TestUser_TableName(t *testing.T) {
user := User{}
expectedTableName := "User"
if tableName := user.TableName(); tableName != expectedTableName {
t.Fatalf("expected %s, but got %s", expectedTableName, tableName)
type fields struct {
BaseModel BaseModel[UserID]
Favorites []UserFavorite
Sources []UserSource
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "Test 1: Is name User",
fields: fields{},
want: "User",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
us := User{
BaseModel: tt.fields.BaseModel,
Favorites: tt.fields.Favorites,
Sources: tt.fields.Sources,
}
if got := us.TableName(); got != tt.want {
t.Errorf("TableName() = %v, want %v", got, tt.want)
}
})
}
}