From c1375ca5c1d5a8a645151cedfced5a336f6b7467 Mon Sep 17 00:00:00 2001 From: Daenney Date: Sun, 6 Aug 2023 12:22:40 +0200 Subject: [PATCH] [chore] Remove go-playground/validator (#2069) * [chore] Remove go-playground/validator It turns out we're not actually using the validator code. This is a remnant from when we intended to use it, but the presence of it and its struct tags creates the illusion we're validating a lot of things we're not. It resulted in some confusion when we were trying to figure out language valdiation. Remove all this code, so that only the validation functions from the validate package we actually use remain. I'm not touching the struct tags in the migrations in order to avoid things potentially thinking migrations need to be re-run. * [chore] Bring back a struct tag on api The validate on internal/api is Gin doing form validation, not the validator from go-playground/validator. --- go.mod | 2 +- internal/gtsmodel/account.go | 112 +++---- internal/gtsmodel/accountnote.go | 16 +- internal/gtsmodel/admin.go | 22 +- internal/gtsmodel/application.go | 18 +- internal/gtsmodel/block.go | 16 +- internal/gtsmodel/client.go | 12 +- internal/gtsmodel/domainblock.go | 20 +- internal/gtsmodel/emaildomainblock.go | 12 +- internal/gtsmodel/emoji.go | 44 +-- internal/gtsmodel/emojicategory.go | 8 +- internal/gtsmodel/follow.go | 20 +- internal/gtsmodel/followrequest.go | 20 +- internal/gtsmodel/instance.go | 36 +-- internal/gtsmodel/list.go | 28 +- internal/gtsmodel/marker.go | 10 +- internal/gtsmodel/mediaattachment.go | 84 ++--- internal/gtsmodel/mention.go | 28 +- internal/gtsmodel/notification.go | 22 +- internal/gtsmodel/report.go | 32 +- internal/gtsmodel/routersession.go | 10 +- internal/gtsmodel/status.go | 98 +++--- internal/gtsmodel/statusbookmark.go | 18 +- internal/gtsmodel/statusfave.go | 20 +- internal/gtsmodel/statusmute.go | 18 +- internal/gtsmodel/tag.go | 12 +- internal/gtsmodel/token.go | 36 +-- internal/gtsmodel/tombstone.go | 10 +- internal/gtsmodel/user.go | 62 ++-- internal/validate/account_test.go | 343 --------------------- internal/validate/application_test.go | 132 -------- internal/validate/block_test.go | 115 ------- internal/validate/client_test.go | 101 ------ internal/validate/domainblock_test.go | 122 -------- internal/validate/emaildomainblock_test.go | 96 ------ internal/validate/emoji_test.go | 195 ------------ internal/validate/follow_test.go | 87 ------ internal/validate/followrequest_test.go | 87 ------ internal/validate/instance_test.go | 145 --------- internal/validate/mediaattachment_test.go | 230 -------------- internal/validate/mention_test.go | 101 ------ internal/validate/notification_test.go | 97 ------ internal/validate/routersession_test.go | 87 ------ internal/validate/status_test.go | 160 ---------- internal/validate/statusbookmark_test.go | 87 ------ internal/validate/statusfave_test.go | 100 ------ internal/validate/statusmute_test.go | 87 ------ internal/validate/structvalidation.go | 67 ---- internal/validate/structvalidation_test.go | 70 ----- internal/validate/token_test.go | 98 ------ internal/validate/user_test.go | 134 -------- 51 files changed, 423 insertions(+), 3164 deletions(-) delete mode 100644 internal/validate/account_test.go delete mode 100644 internal/validate/application_test.go delete mode 100644 internal/validate/block_test.go delete mode 100644 internal/validate/client_test.go delete mode 100644 internal/validate/domainblock_test.go delete mode 100644 internal/validate/emaildomainblock_test.go delete mode 100644 internal/validate/emoji_test.go delete mode 100644 internal/validate/follow_test.go delete mode 100644 internal/validate/followrequest_test.go delete mode 100644 internal/validate/instance_test.go delete mode 100644 internal/validate/mediaattachment_test.go delete mode 100644 internal/validate/mention_test.go delete mode 100644 internal/validate/notification_test.go delete mode 100644 internal/validate/routersession_test.go delete mode 100644 internal/validate/status_test.go delete mode 100644 internal/validate/statusbookmark_test.go delete mode 100644 internal/validate/statusfave_test.go delete mode 100644 internal/validate/statusmute_test.go delete mode 100644 internal/validate/structvalidation.go delete mode 100644 internal/validate/structvalidation_test.go delete mode 100644 internal/validate/token_test.go delete mode 100644 internal/validate/user_test.go diff --git a/go.mod b/go.mod index 999068087..391d8c126 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,6 @@ require ( github.com/gin-gonic/gin v1.9.1 github.com/go-fed/httpsig v1.1.0 github.com/go-playground/form/v4 v4.2.1 - github.com/go-playground/validator/v10 v10.14.1 github.com/google/uuid v1.3.0 github.com/gorilla/feeds v1.1.1 github.com/gorilla/websocket v1.5.0 @@ -108,6 +107,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.1 // indirect github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus/v5 v5.0.4 // indirect diff --git a/internal/gtsmodel/account.go b/internal/gtsmodel/account.go index bce9065c1..7b27f076a 100644 --- a/internal/gtsmodel/account.go +++ b/internal/gtsmodel/account.go @@ -32,55 +32,55 @@ import ( // Account represents either a local or a remote fediverse account, gotosocial or otherwise (mastodon, pleroma, etc). type Account struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created. - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item was last updated. - FetchedAt time.Time `validate:"required_with=Domain" bun:"type:timestamptz,nullzero"` // when was item (remote) last fetched. - Username string `validate:"required" bun:",nullzero,notnull,unique:usernamedomain"` // Username of the account, should just be a string of [a-zA-Z0-9_]. Can be added to domain to create the full username in the form ``[username]@[domain]`` eg., ``user_96@example.org``. Username and domain should be unique *with* each other - Domain string `validate:"omitempty,fqdn" bun:",nullzero,unique:usernamedomain"` // Domain of the account, will be null if this is a local account, otherwise something like ``example.org``. Should be unique with username. - AvatarMediaAttachmentID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Database ID of the media attachment, if present - AvatarMediaAttachment *MediaAttachment `validate:"-" bun:"rel:belongs-to"` // MediaAttachment corresponding to avatarMediaAttachmentID - AvatarRemoteURL string `validate:"omitempty,url" bun:",nullzero"` // For a non-local account, where can the header be fetched? - HeaderMediaAttachmentID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Database ID of the media attachment, if present - HeaderMediaAttachment *MediaAttachment `validate:"-" bun:"rel:belongs-to"` // MediaAttachment corresponding to headerMediaAttachmentID - HeaderRemoteURL string `validate:"omitempty,url" bun:",nullzero"` // For a non-local account, where can the header be fetched? - DisplayName string `validate:"-" bun:""` // DisplayName for this account. Can be empty, then just the Username will be used for display purposes. - EmojiIDs []string `validate:"dive,ulid" bun:"emojis,array"` // Database IDs of any emojis used in this account's bio, display name, etc - Emojis []*Emoji `validate:"-" bun:"attached_emojis,m2m:account_to_emojis"` // Emojis corresponding to emojiIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation - Fields []*Field `validate:"-"` // A slice of of fields that this account has added to their profile. - FieldsRaw []*Field `validate:"-"` // The raw (unparsed) content of fields that this account has added to their profile, without conversion to HTML, only available when requester = target - Note string `validate:"-" bun:""` // A note that this account has on their profile (ie., the account's bio/description of themselves) - NoteRaw string `validate:"-" bun:""` // The raw contents of .Note without conversion to HTML, only available when requester = target - Memorial *bool `validate:"-" bun:",default:false"` // Is this a memorial account, ie., has the user passed away? - AlsoKnownAs string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // This account is associated with x account id (TODO: migrate to be AlsoKnownAsID) - MovedToAccountID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // This account has moved this account id in the database - Bot *bool `validate:"-" bun:",default:false"` // Does this account identify itself as a bot? - Reason string `validate:"-" bun:""` // What reason was given for signing up when this account was created? - Locked *bool `validate:"-" bun:",default:true"` // Does this account need an approval for new followers? - Discoverable *bool `validate:"-" bun:",default:false"` // Should this account be shown in the instance's profile directory? - Privacy Visibility `validate:"required_without=Domain,omitempty,oneof=public unlocked followers_only mutuals_only direct" bun:",nullzero"` // Default post privacy for this account - Sensitive *bool `validate:"-" bun:",default:false"` // Set posts from this account to sensitive by default? - Language string `validate:"omitempty,bcp47_language_tag" bun:",nullzero,notnull,default:'en'"` // What language does this account post in? - StatusContentType string `validate:"required_without=Domain,omitempty,oneof=text/plain text/markdown" bun:",nullzero"` // What is the default format for statuses posted by this account (only for local accounts). - CustomCSS string `validate:"-" bun:",nullzero"` // Custom CSS that should be displayed for this Account's profile and statuses. - URI string `validate:"required,url" bun:",nullzero,notnull,unique"` // ActivityPub URI for this account. - URL string `validate:"required_without=Domain,omitempty,url" bun:",nullzero,unique"` // Web URL for this account's profile - InboxURI string `validate:"required_without=Domain,omitempty,url" bun:",nullzero,unique"` // Address of this account's ActivityPub inbox, for sending activity to - SharedInboxURI *string `validate:"-" bun:""` // Address of this account's ActivityPub sharedInbox. Gotcha warning: this is a string pointer because it has three possible states: 1. We don't know yet if the account has a shared inbox -- null. 2. We know it doesn't have a shared inbox -- empty string. 3. We know it does have a shared inbox -- url string. - OutboxURI string `validate:"required_without=Domain,omitempty,url" bun:",nullzero,unique"` // Address of this account's activitypub outbox - FollowingURI string `validate:"required_without=Domain,omitempty,url" bun:",nullzero,unique"` // URI for getting the following list of this account - FollowersURI string `validate:"required_without=Domain,omitempty,url" bun:",nullzero,unique"` // URI for getting the followers list of this account - FeaturedCollectionURI string `validate:"required_without=Domain,omitempty,url" bun:",nullzero,unique"` // URL for getting the featured collection list of this account - ActorType string `validate:"oneof=Application Group Organization Person Service" bun:",nullzero,notnull"` // What type of activitypub actor is this account? - PrivateKey *rsa.PrivateKey `validate:"required_without=Domain" bun:""` // Privatekey for validating activitypub requests, will only be defined for local accounts - PublicKey *rsa.PublicKey `validate:"required" bun:",notnull"` // Publickey for encoding activitypub requests, will be defined for both local and remote accounts - PublicKeyURI string `validate:"required,url" bun:",nullzero,notnull,unique"` // Web-reachable location of this account's public key - SensitizedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When was this account set to have all its media shown as sensitive? - SilencedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When was this account silenced (eg., statuses only visible to followers, not public)? - SuspendedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When was this account suspended (eg., don't allow it to log in/post, don't accept media/posts from this account) - HideCollections *bool `validate:"-" bun:",default:false"` // Hide this account's collections - SuspensionOrigin string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the database entry that caused this account to become suspended -- can be an account ID or a domain block ID - EnableRSS *bool `validate:"-" bun:",default:false"` // enable RSS feed subscription for this account's public posts at [URL]/feed + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created. + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item was last updated. + FetchedAt time.Time `bun:"type:timestamptz,nullzero"` // when was item (remote) last fetched. + Username string `bun:",nullzero,notnull,unique:usernamedomain"` // Username of the account, should just be a string of [a-zA-Z0-9_]. Can be added to domain to create the full username in the form ``[username]@[domain]`` eg., ``user_96@example.org``. Username and domain should be unique *with* each other + Domain string `bun:",nullzero,unique:usernamedomain"` // Domain of the account, will be null if this is a local account, otherwise something like ``example.org``. Should be unique with username. + AvatarMediaAttachmentID string `bun:"type:CHAR(26),nullzero"` // Database ID of the media attachment, if present + AvatarMediaAttachment *MediaAttachment `bun:"rel:belongs-to"` // MediaAttachment corresponding to avatarMediaAttachmentID + AvatarRemoteURL string `bun:",nullzero"` // For a non-local account, where can the header be fetched? + HeaderMediaAttachmentID string `bun:"type:CHAR(26),nullzero"` // Database ID of the media attachment, if present + HeaderMediaAttachment *MediaAttachment `bun:"rel:belongs-to"` // MediaAttachment corresponding to headerMediaAttachmentID + HeaderRemoteURL string `bun:",nullzero"` // For a non-local account, where can the header be fetched? + DisplayName string `bun:""` // DisplayName for this account. Can be empty, then just the Username will be used for display purposes. + EmojiIDs []string `bun:"emojis,array"` // Database IDs of any emojis used in this account's bio, display name, etc + Emojis []*Emoji `bun:"attached_emojis,m2m:account_to_emojis"` // Emojis corresponding to emojiIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation + Fields []*Field // A slice of of fields that this account has added to their profile. + FieldsRaw []*Field // The raw (unparsed) content of fields that this account has added to their profile, without conversion to HTML, only available when requester = target + Note string `bun:""` // A note that this account has on their profile (ie., the account's bio/description of themselves) + NoteRaw string `bun:""` // The raw contents of .Note without conversion to HTML, only available when requester = target + Memorial *bool `bun:",default:false"` // Is this a memorial account, ie., has the user passed away? + AlsoKnownAs string `bun:"type:CHAR(26),nullzero"` // This account is associated with x account id (TODO: migrate to be AlsoKnownAsID) + MovedToAccountID string `bun:"type:CHAR(26),nullzero"` // This account has moved this account id in the database + Bot *bool `bun:",default:false"` // Does this account identify itself as a bot? + Reason string `bun:""` // What reason was given for signing up when this account was created? + Locked *bool `bun:",default:true"` // Does this account need an approval for new followers? + Discoverable *bool `bun:",default:false"` // Should this account be shown in the instance's profile directory? + Privacy Visibility `bun:",nullzero"` // Default post privacy for this account + Sensitive *bool `bun:",default:false"` // Set posts from this account to sensitive by default? + Language string `bun:",nullzero,notnull,default:'en'"` // What language does this account post in? + StatusContentType string `bun:",nullzero"` // What is the default format for statuses posted by this account (only for local accounts). + CustomCSS string `bun:",nullzero"` // Custom CSS that should be displayed for this Account's profile and statuses. + URI string `bun:",nullzero,notnull,unique"` // ActivityPub URI for this account. + URL string `bun:",nullzero,unique"` // Web URL for this account's profile + InboxURI string `bun:",nullzero,unique"` // Address of this account's ActivityPub inbox, for sending activity to + SharedInboxURI *string `bun:""` // Address of this account's ActivityPub sharedInbox. Gotcha warning: this is a string pointer because it has three possible states: 1. We don't know yet if the account has a shared inbox -- null. 2. We know it doesn't have a shared inbox -- empty string. 3. We know it does have a shared inbox -- url string. + OutboxURI string `bun:",nullzero,unique"` // Address of this account's activitypub outbox + FollowingURI string `bun:",nullzero,unique"` // URI for getting the following list of this account + FollowersURI string `bun:",nullzero,unique"` // URI for getting the followers list of this account + FeaturedCollectionURI string `bun:",nullzero,unique"` // URL for getting the featured collection list of this account + ActorType string `bun:",nullzero,notnull"` // What type of activitypub actor is this account? + PrivateKey *rsa.PrivateKey `bun:""` // Privatekey for validating activitypub requests, will only be defined for local accounts + PublicKey *rsa.PublicKey `bun:",notnull"` // Publickey for encoding activitypub requests, will be defined for both local and remote accounts + PublicKeyURI string `bun:",nullzero,notnull,unique"` // Web-reachable location of this account's public key + SensitizedAt time.Time `bun:"type:timestamptz,nullzero"` // When was this account set to have all its media shown as sensitive? + SilencedAt time.Time `bun:"type:timestamptz,nullzero"` // When was this account silenced (eg., statuses only visible to followers, not public)? + SuspendedAt time.Time `bun:"type:timestamptz,nullzero"` // When was this account suspended (eg., don't allow it to log in/post, don't accept media/posts from this account) + HideCollections *bool `bun:",default:false"` // Hide this account's collections + SuspensionOrigin string `bun:"type:CHAR(26),nullzero"` // id of the database entry that caused this account to become suspended -- can be an account ID or a domain block ID + EnableRSS *bool `bun:",default:false"` // enable RSS feed subscription for this account's public posts at [URL]/feed } // IsLocal returns whether account is a local user account. @@ -131,19 +131,19 @@ func (a *Account) EmojisPopulated() bool { // AccountToEmoji is an intermediate struct to facilitate the many2many relationship between an account and one or more emojis. type AccountToEmoji struct { - AccountID string `validate:"ulid,required" bun:"type:CHAR(26),unique:accountemoji,nullzero,notnull"` - Account *Account `validate:"-" bun:"rel:belongs-to"` - EmojiID string `validate:"ulid,required" bun:"type:CHAR(26),unique:accountemoji,nullzero,notnull"` - Emoji *Emoji `validate:"-" bun:"rel:belongs-to"` + AccountID string `bun:"type:CHAR(26),unique:accountemoji,nullzero,notnull"` + Account *Account `bun:"rel:belongs-to"` + EmojiID string `bun:"type:CHAR(26),unique:accountemoji,nullzero,notnull"` + Emoji *Emoji `bun:"rel:belongs-to"` } // Field represents a key value field on an account, for things like pronouns, website, etc. // VerifiedAt is optional, to be used only if Value is a URL to a webpage that contains the // username of the user. type Field struct { - Name string `validate:"required"` // Name of this field. - Value string `validate:"required"` // Value of this field. - VerifiedAt time.Time `validate:"-" bun:",nullzero"` // This field was verified at (optional). + Name string // Name of this field. + Value string // Value of this field. + VerifiedAt time.Time `bun:",nullzero"` // This field was verified at (optional). } // Relationship describes a requester's relationship with another account. diff --git a/internal/gtsmodel/accountnote.go b/internal/gtsmodel/accountnote.go index 239ed6ce9..74b730eae 100644 --- a/internal/gtsmodel/accountnote.go +++ b/internal/gtsmodel/accountnote.go @@ -21,12 +21,12 @@ import "time" // AccountNote stores a private note from a local account related to any account. type AccountNote struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:account_notes_account_id_target_account_id_uniq,notnull,nullzero"` // ID of the local account that created the note - Account *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to accountID - TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:account_notes_account_id_target_account_id_uniq,notnull,nullzero"` // Who is the target of this note? - TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to targetAccountID - Comment string `validate:"-" bun:""` // The text of the note. + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + AccountID string `bun:"type:CHAR(26),unique:account_notes_account_id_target_account_id_uniq,notnull,nullzero"` // ID of the local account that created the note + Account *Account `bun:"rel:belongs-to"` // Account corresponding to accountID + TargetAccountID string `bun:"type:CHAR(26),unique:account_notes_account_id_target_account_id_uniq,notnull,nullzero"` // Who is the target of this note? + TargetAccount *Account `bun:"rel:belongs-to"` // Account corresponding to targetAccountID + Comment string `bun:""` // The text of the note. } diff --git a/internal/gtsmodel/admin.go b/internal/gtsmodel/admin.go index 22a38f32c..f1f30db2d 100644 --- a/internal/gtsmodel/admin.go +++ b/internal/gtsmodel/admin.go @@ -24,17 +24,17 @@ import ( // AdminAccountAction models an action taken by an instance administrator on an account. type AdminAccountAction struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),notnull,nullzero"` // Who performed this admin action. - Account *Account `validate:"-" bun:"rel:has-one"` // Account corresponding to accountID - TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),notnull,nullzero"` // Who is the target of this action - TargetAccount *Account `validate:"-" bun:"rel:has-one"` // Account corresponding to targetAccountID - Text string `validate:"-" bun:""` // text explaining why this action was taken - Type AdminActionType `validate:"oneof=disable silence suspend" bun:",nullzero,notnull"` // type of action that was taken - SendEmail bool `validate:"-" bun:""` // should an email be sent to the account owner to explain what happened - ReportID string `validate:",omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of a report connected to this action, if it exists + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + AccountID string `bun:"type:CHAR(26),notnull,nullzero"` // Who performed this admin action. + Account *Account `bun:"rel:has-one"` // Account corresponding to accountID + TargetAccountID string `bun:"type:CHAR(26),notnull,nullzero"` // Who is the target of this action + TargetAccount *Account `bun:"rel:has-one"` // Account corresponding to targetAccountID + Text string `bun:""` // text explaining why this action was taken + Type AdminActionType `bun:",nullzero,notnull"` // type of action that was taken + SendEmail bool `bun:""` // should an email be sent to the account owner to explain what happened + ReportID string `bun:"type:CHAR(26),nullzero"` // id of a report connected to this action, if it exists } // AdminActionType describes a type of action taken on an entity by an admin diff --git a/internal/gtsmodel/application.go b/internal/gtsmodel/application.go index a9057b558..5f2d4f4b1 100644 --- a/internal/gtsmodel/application.go +++ b/internal/gtsmodel/application.go @@ -22,13 +22,13 @@ import "time" // Application represents an application that can perform actions on behalf of a user. // It is used to authorize tokens etc, and is associated with an oauth client id in the database. type Application struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - Name string `validate:"required" bun:",notnull"` // name of the application given when it was created (eg., 'tusky') - Website string `validate:"omitempty,url" bun:",nullzero"` // website for the application given when it was created (eg., 'https://tusky.app') - RedirectURI string `validate:"required,uri" bun:",nullzero,notnull"` // redirect uri requested by the application for oauth2 flow - ClientID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // id of the associated oauth client entity in the db - ClientSecret string `validate:"required,uuid" bun:",nullzero,notnull"` // secret of the associated oauth client entity in the db - Scopes string `validate:"required" bun:",notnull"` // scopes requested when this app was created + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + Name string `bun:",notnull"` // name of the application given when it was created (eg., 'tusky') + Website string `bun:",nullzero"` // website for the application given when it was created (eg., 'https://tusky.app') + RedirectURI string `bun:",nullzero,notnull"` // redirect uri requested by the application for oauth2 flow + ClientID string `bun:"type:CHAR(26),nullzero,notnull"` // id of the associated oauth client entity in the db + ClientSecret string `bun:",nullzero,notnull"` // secret of the associated oauth client entity in the db + Scopes string `bun:",notnull"` // scopes requested when this app was created } diff --git a/internal/gtsmodel/block.go b/internal/gtsmodel/block.go index 98f68f451..1fcdb364a 100644 --- a/internal/gtsmodel/block.go +++ b/internal/gtsmodel/block.go @@ -21,12 +21,12 @@ import "time" // Block refers to the blocking of one account by another. type Block struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - URI string `validate:"required,url" bun:",notnull,nullzero,unique"` // ActivityPub uri of this block. - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:blocksrctarget,notnull,nullzero"` // Who does this block originate from? - Account *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to accountID - TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:blocksrctarget,notnull,nullzero"` // Who is the target of this block ? - TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to targetAccountID + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + URI string `bun:",notnull,nullzero,unique"` // ActivityPub uri of this block. + AccountID string `bun:"type:CHAR(26),unique:blocksrctarget,notnull,nullzero"` // Who does this block originate from? + Account *Account `bun:"rel:belongs-to"` // Account corresponding to accountID + TargetAccountID string `bun:"type:CHAR(26),unique:blocksrctarget,notnull,nullzero"` // Who is the target of this block ? + TargetAccount *Account `bun:"rel:belongs-to"` // Account corresponding to targetAccountID } diff --git a/internal/gtsmodel/client.go b/internal/gtsmodel/client.go index a712257e5..35a85fdbe 100644 --- a/internal/gtsmodel/client.go +++ b/internal/gtsmodel/client.go @@ -21,10 +21,10 @@ import "time" // Client is a wrapper for OAuth client details. type Client struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - Secret string `validate:"required,uuid" bun:",nullzero,notnull"` // secret generated when client was created - Domain string `validate:"required,uri" bun:",nullzero,notnull"` // domain requested for client - UserID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the user that this client acts on behalf of + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + Secret string `bun:",nullzero,notnull"` // secret generated when client was created + Domain string `bun:",nullzero,notnull"` // domain requested for client + UserID string `bun:"type:CHAR(26),nullzero"` // id of the user that this client acts on behalf of } diff --git a/internal/gtsmodel/domainblock.go b/internal/gtsmodel/domainblock.go index 0d038d18c..dfe642ef5 100644 --- a/internal/gtsmodel/domainblock.go +++ b/internal/gtsmodel/domainblock.go @@ -21,14 +21,14 @@ import "time" // DomainBlock represents a federation block against a particular domain type DomainBlock struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - Domain string `validate:"required,fqdn" bun:",nullzero,notnull"` // domain to block. Eg. 'whatever.com' - CreatedByAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // Account ID of the creator of this block - CreatedByAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to createdByAccountID - PrivateComment string `validate:"-" bun:""` // Private comment on this block, viewable to admins - PublicComment string `validate:"-" bun:""` // Public comment on this block, viewable (optionally) by everyone - Obfuscate *bool `validate:"-" bun:",nullzero,notnull,default:false"` // whether the domain name should appear obfuscated when displaying it publicly - SubscriptionID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // if this block was created through a subscription, what's the subscription ID? + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + Domain string `bun:",nullzero,notnull"` // domain to block. Eg. 'whatever.com' + CreatedByAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // Account ID of the creator of this block + CreatedByAccount *Account `bun:"rel:belongs-to"` // Account corresponding to createdByAccountID + PrivateComment string `bun:""` // Private comment on this block, viewable to admins + PublicComment string `bun:""` // Public comment on this block, viewable (optionally) by everyone + Obfuscate *bool `bun:",nullzero,notnull,default:false"` // whether the domain name should appear obfuscated when displaying it publicly + SubscriptionID string `bun:"type:CHAR(26),nullzero"` // if this block was created through a subscription, what's the subscription ID? } diff --git a/internal/gtsmodel/emaildomainblock.go b/internal/gtsmodel/emaildomainblock.go index 7258f64d7..c81eec365 100644 --- a/internal/gtsmodel/emaildomainblock.go +++ b/internal/gtsmodel/emaildomainblock.go @@ -21,10 +21,10 @@ import "time" // EmailDomainBlock represents a domain that the server should automatically reject sign-up requests from. type EmailDomainBlock struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - Domain string `validate:"required,fqdn" bun:",nullzero,notnull"` // Email domain to block. Eg. 'gmail.com' or 'hotmail.com' - CreatedByAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // Account ID of the creator of this block - CreatedByAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to createdByAccountID + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + Domain string `bun:",nullzero,notnull"` // Email domain to block. Eg. 'gmail.com' or 'hotmail.com' + CreatedByAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // Account ID of the creator of this block + CreatedByAccount *Account `bun:"rel:belongs-to"` // Account corresponding to createdByAccountID } diff --git a/internal/gtsmodel/emoji.go b/internal/gtsmodel/emoji.go index 0fcc3247b..596a64110 100644 --- a/internal/gtsmodel/emoji.go +++ b/internal/gtsmodel/emoji.go @@ -21,26 +21,26 @@ import "time" // Emoji represents a custom emoji that's been uploaded through the admin UI or downloaded from a remote instance. type Emoji struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - Shortcode string `validate:"required" bun:",nullzero,notnull,unique:domainshortcode"` // String shortcode for this emoji -- the part that's between colons. This should be a-zA-Z_ eg., 'blob_hug' 'purple_heart' 'Gay_Otter' Must be unique with domain. - Domain string `validate:"omitempty,fqdn" bun:",nullzero,unique:domainshortcode"` // Origin domain of this emoji, eg 'example.org', 'queer.party'. empty string for local emojis. - ImageRemoteURL string `validate:"required_without=ImageURL,omitempty,url" bun:",nullzero"` // Where can this emoji be retrieved remotely? Null for local emojis. - ImageStaticRemoteURL string `validate:"required_without=ImageStaticURL,omitempty,url" bun:",nullzero"` // Where can a static / non-animated version of this emoji be retrieved remotely? Null for local emojis. - ImageURL string `validate:"required_without=ImageRemoteURL,required_without=Domain,omitempty,url" bun:",nullzero"` // Where can this emoji be retrieved from the local server? Null for remote emojis. - ImageStaticURL string `validate:"required_without=ImageStaticRemoteURL,required_without=Domain,omitempty,url" bun:",nullzero"` // Where can a static version of this emoji be retrieved from the local server? Null for remote emojis. - ImagePath string `validate:"required,file" bun:",nullzero,notnull"` // Path of the emoji image in the server storage system. - ImageStaticPath string `validate:"required,file" bun:",nullzero,notnull"` // Path of a static version of the emoji image in the server storage system - ImageContentType string `validate:"required" bun:",nullzero,notnull"` // MIME content type of the emoji image - ImageStaticContentType string `validate:"required" bun:",nullzero,notnull"` // MIME content type of the static version of the emoji image. - ImageFileSize int `validate:"required,min=1" bun:",nullzero,notnull"` // Size of the emoji image file in bytes, for serving purposes. - ImageStaticFileSize int `validate:"required,min=1" bun:",nullzero,notnull"` // Size of the static version of the emoji image file in bytes, for serving purposes. - ImageUpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When was the emoji image last updated? - Disabled *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Has a moderation action disabled this emoji from being shown? - URI string `validate:"url" bun:",nullzero,notnull,unique"` // ActivityPub uri of this emoji. Something like 'https://example.org/emojis/1234' - VisibleInPicker *bool `validate:"-" bun:",nullzero,notnull,default:true"` // Is this emoji visible in the admin emoji picker? - Category *EmojiCategory `validate:"-" bun:"rel:belongs-to"` // In which emoji category is this emoji visible? - CategoryID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // ID of the category this emoji belongs to. - Cached *bool `validate:"-" bun:",nullzero,notnull,default:false"` + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + Shortcode string `bun:",nullzero,notnull,unique:domainshortcode"` // String shortcode for this emoji -- the part that's between colons. This should be a-zA-Z_ eg., 'blob_hug' 'purple_heart' 'Gay_Otter' Must be unique with domain. + Domain string `bun:",nullzero,unique:domainshortcode"` // Origin domain of this emoji, eg 'example.org', 'queer.party'. empty string for local emojis. + ImageRemoteURL string `bun:",nullzero"` // Where can this emoji be retrieved remotely? Null for local emojis. + ImageStaticRemoteURL string `bun:",nullzero"` // Where can a static / non-animated version of this emoji be retrieved remotely? Null for local emojis. + ImageURL string `bun:",nullzero"` // Where can this emoji be retrieved from the local server? Null for remote emojis. + ImageStaticURL string `bun:",nullzero"` // Where can a static version of this emoji be retrieved from the local server? Null for remote emojis. + ImagePath string `bun:",nullzero,notnull"` // Path of the emoji image in the server storage system. + ImageStaticPath string `bun:",nullzero,notnull"` // Path of a static version of the emoji image in the server storage system + ImageContentType string `bun:",nullzero,notnull"` // MIME content type of the emoji image + ImageStaticContentType string `bun:",nullzero,notnull"` // MIME content type of the static version of the emoji image. + ImageFileSize int `bun:",nullzero,notnull"` // Size of the emoji image file in bytes, for serving purposes. + ImageStaticFileSize int `bun:",nullzero,notnull"` // Size of the static version of the emoji image file in bytes, for serving purposes. + ImageUpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When was the emoji image last updated? + Disabled *bool `bun:",nullzero,notnull,default:false"` // Has a moderation action disabled this emoji from being shown? + URI string `bun:",nullzero,notnull,unique"` // ActivityPub uri of this emoji. Something like 'https://example.org/emojis/1234' + VisibleInPicker *bool `bun:",nullzero,notnull,default:true"` // Is this emoji visible in the admin emoji picker? + Category *EmojiCategory `bun:"rel:belongs-to"` // In which emoji category is this emoji visible? + CategoryID string `bun:"type:CHAR(26),nullzero"` // ID of the category this emoji belongs to. + Cached *bool `bun:",nullzero,notnull,default:false"` } diff --git a/internal/gtsmodel/emojicategory.go b/internal/gtsmodel/emojicategory.go index ad17a8600..80a448f3c 100644 --- a/internal/gtsmodel/emojicategory.go +++ b/internal/gtsmodel/emojicategory.go @@ -21,8 +21,8 @@ import "time" // EmojiCategory represents a grouping of custom emojis. type EmojiCategory struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - Name string `validate:"required" bun:",nullzero,notnull,unique"` // name of this category + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + Name string `bun:",nullzero,notnull,unique"` // name of this category } diff --git a/internal/gtsmodel/follow.go b/internal/gtsmodel/follow.go index 871d01e20..f359a7bc4 100644 --- a/internal/gtsmodel/follow.go +++ b/internal/gtsmodel/follow.go @@ -21,14 +21,14 @@ import "time" // Follow represents one account following another, and the metadata around that follow. type Follow struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - URI string `validate:"required,url" bun:",notnull,nullzero,unique"` // ActivityPub uri of this follow. - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:srctarget,notnull,nullzero"` // Who does this follow originate from? - Account *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to accountID - TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:srctarget,notnull,nullzero"` // Who is the target of this follow ? - TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to targetAccountID - ShowReblogs *bool `validate:"-" bun:",nullzero,notnull,default:true"` // Does this follow also want to see reblogs and not just posts? - Notify *bool `validate:"-" bun:",nullzero,notnull,default:false"` // does the following account want to be notified when the followed account posts? + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + URI string `bun:",notnull,nullzero,unique"` // ActivityPub uri of this follow. + AccountID string `bun:"type:CHAR(26),unique:srctarget,notnull,nullzero"` // Who does this follow originate from? + Account *Account `bun:"rel:belongs-to"` // Account corresponding to accountID + TargetAccountID string `bun:"type:CHAR(26),unique:srctarget,notnull,nullzero"` // Who is the target of this follow ? + TargetAccount *Account `bun:"rel:belongs-to"` // Account corresponding to targetAccountID + ShowReblogs *bool `bun:",nullzero,notnull,default:true"` // Does this follow also want to see reblogs and not just posts? + Notify *bool `bun:",nullzero,notnull,default:false"` // does the following account want to be notified when the followed account posts? } diff --git a/internal/gtsmodel/followrequest.go b/internal/gtsmodel/followrequest.go index 3fc161af7..32eb13950 100644 --- a/internal/gtsmodel/followrequest.go +++ b/internal/gtsmodel/followrequest.go @@ -21,14 +21,14 @@ import "time" // FollowRequest represents one account requesting to follow another, and the metadata around that request. type FollowRequest struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - URI string `validate:"required,url" bun:",notnull,nullzero,unique"` // ActivityPub uri of this follow (request). - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:frsrctarget,notnull,nullzero"` // Who does this follow request originate from? - Account *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to accountID - TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:frsrctarget,notnull,nullzero"` // Who is the target of this follow request? - TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to targetAccountID - ShowReblogs *bool `validate:"-" bun:",nullzero,notnull,default:true"` // Does this follow also want to see reblogs and not just posts? - Notify *bool `validate:"-" bun:",nullzero,notnull,default:false"` // does the following account want to be notified when the followed account posts? + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + URI string `bun:",notnull,nullzero,unique"` // ActivityPub uri of this follow (request). + AccountID string `bun:"type:CHAR(26),unique:frsrctarget,notnull,nullzero"` // Who does this follow request originate from? + Account *Account `bun:"rel:belongs-to"` // Account corresponding to accountID + TargetAccountID string `bun:"type:CHAR(26),unique:frsrctarget,notnull,nullzero"` // Who is the target of this follow request? + TargetAccount *Account `bun:"rel:belongs-to"` // Account corresponding to targetAccountID + ShowReblogs *bool `bun:",nullzero,notnull,default:true"` // Does this follow also want to see reblogs and not just posts? + Notify *bool `bun:",nullzero,notnull,default:false"` // does the following account want to be notified when the followed account posts? } diff --git a/internal/gtsmodel/instance.go b/internal/gtsmodel/instance.go index 3172726fd..388f0f4ed 100644 --- a/internal/gtsmodel/instance.go +++ b/internal/gtsmodel/instance.go @@ -21,22 +21,22 @@ import "time" // Instance represents a federated instance, either local or remote. type Instance struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - Domain string `validate:"required,fqdn" bun:",nullzero,notnull,unique"` // Instance domain eg example.org - Title string `validate:"-" bun:""` // Title of this instance as it would like to be displayed. - URI string `validate:"required,url" bun:",nullzero,notnull,unique"` // base URI of this instance eg https://example.org - SuspendedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When was this instance suspended, if at all? - DomainBlockID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // ID of any existing domain block for this instance in the database - DomainBlock *DomainBlock `validate:"-" bun:"rel:belongs-to"` // Domain block corresponding to domainBlockID - ShortDescription string `validate:"-" bun:""` // Short description of this instance - Description string `validate:"-" bun:""` // Longer description of this instance - Terms string `validate:"-" bun:""` // Terms and conditions of this instance - ContactEmail string `validate:"omitempty,email" bun:""` // Contact email address for this instance - ContactAccountUsername string `validate:"required_with=ContactAccountID" bun:",nullzero"` // Username of the contact account for this instance - ContactAccountID string `validate:"required_with=ContactAccountUsername,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Contact account ID in the database for this instance - ContactAccount *Account `validate:"-" bun:"rel:belongs-to"` // account corresponding to contactAccountID - Reputation int64 `validate:"-" bun:",notnull,default:0"` // Reputation score of this instance - Version string `validate:"-" bun:",nullzero"` // Version of the software used on this instance + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + Domain string `bun:",nullzero,notnull,unique"` // Instance domain eg example.org + Title string `bun:""` // Title of this instance as it would like to be displayed. + URI string `bun:",nullzero,notnull,unique"` // base URI of this instance eg https://example.org + SuspendedAt time.Time `bun:"type:timestamptz,nullzero"` // When was this instance suspended, if at all? + DomainBlockID string `bun:"type:CHAR(26),nullzero"` // ID of any existing domain block for this instance in the database + DomainBlock *DomainBlock `bun:"rel:belongs-to"` // Domain block corresponding to domainBlockID + ShortDescription string `bun:""` // Short description of this instance + Description string `bun:""` // Longer description of this instance + Terms string `bun:""` // Terms and conditions of this instance + ContactEmail string `bun:""` // Contact email address for this instance + ContactAccountUsername string `bun:",nullzero"` // Username of the contact account for this instance + ContactAccountID string `bun:"type:CHAR(26),nullzero"` // Contact account ID in the database for this instance + ContactAccount *Account `bun:"rel:belongs-to"` // account corresponding to contactAccountID + Reputation int64 `bun:",notnull,default:0"` // Reputation score of this instance + Version string `bun:",nullzero"` // Version of the software used on this instance } diff --git a/internal/gtsmodel/list.go b/internal/gtsmodel/list.go index 98188b113..ea53df9b3 100644 --- a/internal/gtsmodel/list.go +++ b/internal/gtsmodel/list.go @@ -21,24 +21,24 @@ import "time" // List refers to a list of follows for which the owning account wants to view a timeline of posts. type List struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - Title string `validate:"required" bun:",nullzero,notnull,unique:listaccounttitle"` // Title of this list. - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),notnull,nullzero,unique:listaccounttitle"` // Account that created/owns the list - Account *Account `validate:"-" bun:"-"` // Account corresponding to accountID - ListEntries []*ListEntry `validate:"-" bun:"-"` // Entries contained by this list. - RepliesPolicy RepliesPolicy `validate:"-" bun:",nullzero,notnull,default:'followed'"` // RepliesPolicy for this list. + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + Title string `bun:",nullzero,notnull,unique:listaccounttitle"` // Title of this list. + AccountID string `bun:"type:CHAR(26),notnull,nullzero,unique:listaccounttitle"` // Account that created/owns the list + Account *Account `bun:"-"` // Account corresponding to accountID + ListEntries []*ListEntry `bun:"-"` // Entries contained by this list. + RepliesPolicy RepliesPolicy `bun:",nullzero,notnull,default:'followed'"` // RepliesPolicy for this list. } // ListEntry refers to a single follow entry in a list. type ListEntry struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - ListID string `validate:"required,ulid" bun:"type:CHAR(26),notnull,nullzero,unique:listentrylistfollow"` // ID of the list that this entry belongs to. - FollowID string `validate:"required,ulid" bun:"type:CHAR(26),notnull,nullzero,unique:listentrylistfollow"` // Follow that the account owning this entry wants to see posts of in the timeline. - Follow *Follow `validate:"-" bun:"-"` // Follow corresponding to followID. + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + ListID string `bun:"type:CHAR(26),notnull,nullzero,unique:listentrylistfollow"` // ID of the list that this entry belongs to. + FollowID string `bun:"type:CHAR(26),notnull,nullzero,unique:listentrylistfollow"` // Follow that the account owning this entry wants to see posts of in the timeline. + Follow *Follow `bun:"-"` // Follow corresponding to followID. } // RepliesPolicy denotes which replies should be shown in the list. diff --git a/internal/gtsmodel/marker.go b/internal/gtsmodel/marker.go index 3aeb376ff..93fbebfe0 100644 --- a/internal/gtsmodel/marker.go +++ b/internal/gtsmodel/marker.go @@ -21,11 +21,11 @@ import "time" // Marker stores a local account's read position on a given timeline. type Marker struct { - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),pk,unique:markers_account_id_timeline_uniq,notnull,nullzero"` // ID of the local account that owns the marker - Name MarkerName `validate:"oneof=home notifications" bun:",nullzero,notnull,pk,unique:markers_account_id_timeline_uniq"` // Name of the marked timeline - UpdatedAt time.Time `validate:"required" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When marker was last updated - Version int `validate:"required,min=0" bun:",nullzero,notnull,default:0"` // For optimistic concurrency control - LastReadID string `validate:"required,ulid" bun:"type:CHAR(26),notnull,nullzero"` // Last ID read on this timeline (status ID for home, notification ID for notifications) + AccountID string `bun:"type:CHAR(26),pk,unique:markers_account_id_timeline_uniq,notnull,nullzero"` // ID of the local account that owns the marker + Name MarkerName `bun:",nullzero,notnull,pk,unique:markers_account_id_timeline_uniq"` // Name of the marked timeline + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When marker was last updated + Version int `bun:",nullzero,notnull,default:0"` // For optimistic concurrency control + LastReadID string `bun:"type:CHAR(26),notnull,nullzero"` // Last ID read on this timeline (status ID for home, notification ID for notifications) } // MarkerName is the name of one of the timelines we can store markers for. diff --git a/internal/gtsmodel/mediaattachment.go b/internal/gtsmodel/mediaattachment.go index b8e8b57f7..e418de7d2 100644 --- a/internal/gtsmodel/mediaattachment.go +++ b/internal/gtsmodel/mediaattachment.go @@ -24,42 +24,42 @@ import ( // MediaAttachment represents a user-uploaded media attachment: an image/video/audio/gif that is // somewhere in storage and that can be retrieved and served by the router. type MediaAttachment struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - StatusID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // ID of the status to which this is attached - URL string `validate:"required_without=RemoteURL,omitempty,url" bun:",nullzero"` // Where can the attachment be retrieved on *this* server - RemoteURL string `validate:"required_without=URL,omitempty,url" bun:",nullzero"` // Where can the attachment be retrieved on a remote server (empty for local media) - Type FileType `validate:"oneof=Image Gifv Audio Video Unknown" bun:",nullzero,notnull"` // Type of file (image/gifv/audio/video) - FileMeta FileMeta `validate:"required" bun:",embed:,nullzero,notnull"` // Metadata about the file - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // To which account does this attachment belong - Description string `validate:"-" bun:""` // Description of the attachment (for screenreaders) - ScheduledStatusID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // To which scheduled status does this attachment belong - Blurhash string `validate:"required_if=Type Image,required_if=Type Gif,required_if=Type Video" bun:",nullzero"` // What is the generated blurhash of this attachment - Processing ProcessingStatus `validate:"oneof=0 1 2 666" bun:",notnull,default:2"` // What is the processing status of this attachment - File File `validate:"required" bun:",embed:file_,notnull,nullzero"` // metadata for the whole file - Thumbnail Thumbnail `validate:"required" bun:",embed:thumbnail_,notnull,nullzero"` // small image thumbnail derived from a larger image, video, or audio file. - Avatar *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this attachment being used as an avatar? - Header *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this attachment being used as a header? - Cached *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this attachment currently cached by our instance? + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + StatusID string `bun:"type:CHAR(26),nullzero"` // ID of the status to which this is attached + URL string `bun:",nullzero"` // Where can the attachment be retrieved on *this* server + RemoteURL string `bun:",nullzero"` // Where can the attachment be retrieved on a remote server (empty for local media) + Type FileType `bun:",nullzero,notnull"` // Type of file (image/gifv/audio/video) + FileMeta FileMeta `bun:",embed:,nullzero,notnull"` // Metadata about the file + AccountID string `bun:"type:CHAR(26),nullzero,notnull"` // To which account does this attachment belong + Description string `bun:""` // Description of the attachment (for screenreaders) + ScheduledStatusID string `bun:"type:CHAR(26),nullzero"` // To which scheduled status does this attachment belong + Blurhash string `bun:",nullzero"` // What is the generated blurhash of this attachment + Processing ProcessingStatus `bun:",notnull,default:2"` // What is the processing status of this attachment + File File `bun:",embed:file_,notnull,nullzero"` // metadata for the whole file + Thumbnail Thumbnail `bun:",embed:thumbnail_,notnull,nullzero"` // small image thumbnail derived from a larger image, video, or audio file. + Avatar *bool `bun:",nullzero,notnull,default:false"` // Is this attachment being used as an avatar? + Header *bool `bun:",nullzero,notnull,default:false"` // Is this attachment being used as a header? + Cached *bool `bun:",nullzero,notnull,default:false"` // Is this attachment currently cached by our instance? } // File refers to the metadata for the whole file type File struct { - Path string `validate:"required,file" bun:",nullzero,notnull"` // Path of the file in storage. - ContentType string `validate:"required" bun:",nullzero,notnull"` // MIME content type of the file. - FileSize int `validate:"required" bun:",notnull"` // File size in bytes - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When was the file last updated. + Path string `bun:",nullzero,notnull"` // Path of the file in storage. + ContentType string `bun:",nullzero,notnull"` // MIME content type of the file. + FileSize int `bun:",notnull"` // File size in bytes + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When was the file last updated. } // Thumbnail refers to a small image thumbnail derived from a larger image, video, or audio file. type Thumbnail struct { - Path string `validate:"required,file" bun:",nullzero,notnull"` // Path of the file in storage. - ContentType string `validate:"required" bun:",nullzero,notnull"` // MIME content type of the file. - FileSize int `validate:"required" bun:",notnull"` // File size in bytes - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When was the file last updated. - URL string `validate:"required_without=RemoteURL,omitempty,url" bun:",nullzero"` // What is the URL of the thumbnail on the local server - RemoteURL string `validate:"required_without=URL,omitempty,url" bun:",nullzero"` // What is the remote URL of the thumbnail (empty for local media) + Path string `bun:",nullzero,notnull"` // Path of the file in storage. + ContentType string `bun:",nullzero,notnull"` // MIME content type of the file. + FileSize int `bun:",notnull"` // File size in bytes + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When was the file last updated. + URL string `bun:",nullzero"` // What is the URL of the thumbnail on the local server + RemoteURL string `bun:",nullzero"` // What is the remote URL of the thumbnail (empty for local media) } // ProcessingStatus refers to how far along in the processing stage the attachment is. @@ -87,33 +87,33 @@ const ( // FileMeta describes metadata about the actual contents of the file. type FileMeta struct { - Original Original `validate:"required" bun:"embed:original_"` + Original Original `bun:"embed:original_"` Small Small `bun:"embed:small_"` Focus Focus `bun:"embed:focus_"` } // Small can be used for a thumbnail of any media type type Small struct { - Width int `validate:"required_with=Height Size Aspect"` // width in pixels - Height int `validate:"required_with=Width Size Aspect"` // height in pixels - Size int `validate:"required_with=Width Height Aspect"` // size in pixels (width * height) - Aspect float32 `validate:"required_with=Width Height Size"` // aspect ratio (width / height) + Width int // width in pixels + Height int // height in pixels + Size int // size in pixels (width * height) + Aspect float32 // aspect ratio (width / height) } // Original can be used for original metadata for any media type type Original struct { - Width int `validate:"required_with=Height Size Aspect"` // width in pixels - Height int `validate:"required_with=Width Size Aspect"` // height in pixels - Size int `validate:"required_with=Width Height Aspect"` // size in pixels (width * height) - Aspect float32 `validate:"required_with=Width Height Size"` // aspect ratio (width / height) - Duration *float32 `validate:"-"` // video-specific: duration of the video in seconds - Framerate *float32 `validate:"-"` // video-specific: fps - Bitrate *uint64 `validate:"-"` // video-specific: bitrate + Width int // width in pixels + Height int // height in pixels + Size int // size in pixels (width * height) + Aspect float32 // aspect ratio (width / height) + Duration *float32 // video-specific: duration of the video in seconds + Framerate *float32 // video-specific: fps + Bitrate *uint64 // video-specific: bitrate } // Focus describes the 'center' of the image for display purposes. // X and Y should each be between -1 and 1 type Focus struct { - X float32 `validate:"omitempty,max=1,min=-1"` - Y float32 `validate:"omitempty,max=1,min=-1"` + X float32 + Y float32 } diff --git a/internal/gtsmodel/mention.go b/internal/gtsmodel/mention.go index 9ad9204eb..fb7f3b51a 100644 --- a/internal/gtsmodel/mention.go +++ b/internal/gtsmodel/mention.go @@ -24,17 +24,17 @@ import ( // Mention refers to the 'tagging' or 'mention' of a user within a status. type Mention struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - StatusID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // ID of the status this mention originates from - Status *Status `validate:"-" bun:"rel:belongs-to"` // status referred to by statusID - OriginAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // ID of the mention creator account - OriginAccountURI string `validate:"url" bun:",nullzero,notnull"` // ActivityPub URI of the originator/creator of the mention - OriginAccount *Account `validate:"-" bun:"rel:belongs-to"` // account referred to by originAccountID - TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // Mention target/receiver account ID - TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // account referred to by targetAccountID - Silent *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Prevent this mention from generating a notification? + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + StatusID string `bun:"type:CHAR(26),nullzero,notnull"` // ID of the status this mention originates from + Status *Status `bun:"rel:belongs-to"` // status referred to by statusID + OriginAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // ID of the mention creator account + OriginAccountURI string `bun:",nullzero,notnull"` // ActivityPub URI of the originator/creator of the mention + OriginAccount *Account `bun:"rel:belongs-to"` // account referred to by originAccountID + TargetAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // Mention target/receiver account ID + TargetAccount *Account `bun:"rel:belongs-to"` // account referred to by targetAccountID + Silent *bool `bun:",nullzero,notnull,default:false"` // Prevent this mention from generating a notification? /* NON-DATABASE CONVENIENCE FIELDS @@ -48,15 +48,15 @@ type Mention struct { // @whatever_username@example.org // // This will not be put in the database, it's just for convenience. - NameString string `validate:"-" bun:"-"` + NameString string `bun:"-"` // TargetAccountURI is the AP ID (uri) of the user mentioned. // // This will not be put in the database, it's just for convenience. - TargetAccountURI string `validate:"-" bun:"-"` + TargetAccountURI string `bun:"-"` // TargetAccountURL is the web url of the user mentioned. // // This will not be put in the database, it's just for convenience. - TargetAccountURL string `validate:"-" bun:"-"` + TargetAccountURL string `bun:"-"` // A pointer to the gtsmodel account of the mentioned account. } diff --git a/internal/gtsmodel/notification.go b/internal/gtsmodel/notification.go index 7e8db7713..5e2eff167 100644 --- a/internal/gtsmodel/notification.go +++ b/internal/gtsmodel/notification.go @@ -21,17 +21,17 @@ import "time" // Notification models an alert/notification sent to an account about something like a reblog, like, new follow request, etc. type Notification struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - NotificationType NotificationType `validate:"oneof=follow follow_request mention reblog favourite poll status" bun:",nullzero,notnull"` // Type of this notification - TargetAccountID string `validate:"ulid" bun:"type:CHAR(26),nullzero,notnull"` // ID of the account targeted by the notification (ie., who will receive the notification?) - TargetAccount *Account `validate:"-" bun:"-"` // Account corresponding to TargetAccountID. Can be nil, always check first + select using ID if necessary. - OriginAccountID string `validate:"ulid" bun:"type:CHAR(26),nullzero,notnull"` // ID of the account that performed the action that created the notification. - OriginAccount *Account `validate:"-" bun:"-"` // Account corresponding to OriginAccountID. Can be nil, always check first + select using ID if necessary. - StatusID string `validate:"required_if=NotificationType mention,required_if=NotificationType reblog,required_if=NotificationType favourite,required_if=NotificationType status,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // If the notification pertains to a status, what is the database ID of that status? - Status *Status `validate:"-" bun:"-"` // Status corresponding to StatusID. Can be nil, always check first + select using ID if necessary. - Read *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Notification has been seen/read + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + NotificationType NotificationType `bun:",nullzero,notnull"` // Type of this notification + TargetAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // ID of the account targeted by the notification (ie., who will receive the notification?) + TargetAccount *Account `bun:"-"` // Account corresponding to TargetAccountID. Can be nil, always check first + select using ID if necessary. + OriginAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // ID of the account that performed the action that created the notification. + OriginAccount *Account `bun:"-"` // Account corresponding to OriginAccountID. Can be nil, always check first + select using ID if necessary. + StatusID string `bun:"type:CHAR(26),nullzero"` // If the notification pertains to a status, what is the database ID of that status? + Status *Status `bun:"-"` // Status corresponding to StatusID. Can be nil, always check first + select using ID if necessary. + Read *bool `bun:",nullzero,notnull,default:false"` // Notification has been seen/read } // NotificationType describes the reason/type of this notification. diff --git a/internal/gtsmodel/report.go b/internal/gtsmodel/report.go index eb45c820c..e5b942563 100644 --- a/internal/gtsmodel/report.go +++ b/internal/gtsmodel/report.go @@ -26,20 +26,20 @@ import "time" // or another instance, OR a report that was created remotely (on another instance) // about a user on this instance, and received via the federated (s2s) API. type Report struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - URI string `validate:"required,url" bun:",unique,nullzero,notnull"` // activitypub URI of this report - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // which account created this report - Account *Account `validate:"-" bun:"-"` // account corresponding to AccountID - TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // which account is targeted by this report - TargetAccount *Account `validate:"-" bun:"-"` // account corresponding to TargetAccountID - Comment string `validate:"-" bun:",nullzero"` // comment / explanation for this report, by the reporter - StatusIDs []string `validate:"dive,ulid" bun:"statuses,array"` // database IDs of any statuses referenced by this report - Statuses []*Status `validate:"-" bun:"-"` // statuses corresponding to StatusIDs - Forwarded *bool `validate:"-" bun:",nullzero,notnull,default:false"` // flag to indicate report should be forwarded to remote instance - ActionTaken string `validate:"-" bun:",nullzero"` // string description of what action was taken in response to this report - ActionTakenAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // time at which action was taken, if any - ActionTakenByAccountID string `validate:",omitempty,ulid" bun:"type:CHAR(26),nullzero"` // database ID of account which took action, if any - ActionTakenByAccount *Account `validate:"-" bun:"-"` // account corresponding to ActionTakenByID, if any + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + URI string `bun:",unique,nullzero,notnull"` // activitypub URI of this report + AccountID string `bun:"type:CHAR(26),nullzero,notnull"` // which account created this report + Account *Account `bun:"-"` // account corresponding to AccountID + TargetAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // which account is targeted by this report + TargetAccount *Account `bun:"-"` // account corresponding to TargetAccountID + Comment string `bun:",nullzero"` // comment / explanation for this report, by the reporter + StatusIDs []string `bun:"statuses,array"` // database IDs of any statuses referenced by this report + Statuses []*Status `bun:"-"` // statuses corresponding to StatusIDs + Forwarded *bool `bun:",nullzero,notnull,default:false"` // flag to indicate report should be forwarded to remote instance + ActionTaken string `bun:",nullzero"` // string description of what action was taken in response to this report + ActionTakenAt time.Time `bun:"type:timestamptz,nullzero"` // time at which action was taken, if any + ActionTakenByAccountID string `bun:"type:CHAR(26),nullzero"` // database ID of account which took action, if any + ActionTakenByAccount *Account `bun:"-"` // account corresponding to ActionTakenByID, if any } diff --git a/internal/gtsmodel/routersession.go b/internal/gtsmodel/routersession.go index 050c0715b..d51d93bca 100644 --- a/internal/gtsmodel/routersession.go +++ b/internal/gtsmodel/routersession.go @@ -21,9 +21,9 @@ import "time" // RouterSession is used to store and retrieve settings for a router session. type RouterSession struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - Auth []byte `validate:"required,len=32" bun:"type:bytea,notnull,nullzero"` - Crypt []byte `validate:"required,len=32" bun:"type:bytea,notnull,nullzero"` + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + Auth []byte `bun:"type:bytea,notnull,nullzero"` + Crypt []byte `bun:"type:bytea,notnull,nullzero"` } diff --git a/internal/gtsmodel/status.go b/internal/gtsmodel/status.go index 393bb1ac7..3e8880798 100644 --- a/internal/gtsmodel/status.go +++ b/internal/gtsmodel/status.go @@ -25,47 +25,47 @@ import ( // Status represents a user-created 'post' or 'status' in the database, either remote or local type Status struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - FetchedAt time.Time `validate:"required_with=!Local" bun:"type:timestamptz,nullzero"` // when was item (remote) last fetched. - PinnedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // Status was pinned by owning account at this time. - URI string `validate:"required,url" bun:",unique,nullzero,notnull"` // activitypub URI of this status - URL string `validate:"url" bun:",nullzero"` // web url for viewing this status - Content string `validate:"-" bun:""` // content of this status; likely html-formatted but not guaranteed - AttachmentIDs []string `validate:"dive,ulid" bun:"attachments,array"` // Database IDs of any media attachments associated with this status - Attachments []*MediaAttachment `validate:"-" bun:"attached_media,rel:has-many"` // Attachments corresponding to attachmentIDs - TagIDs []string `validate:"dive,ulid" bun:"tags,array"` // Database IDs of any tags used in this status - Tags []*Tag `validate:"-" bun:"attached_tags,m2m:status_to_tags"` // Tags corresponding to tagIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation - MentionIDs []string `validate:"dive,ulid" bun:"mentions,array"` // Database IDs of any mentions in this status - Mentions []*Mention `validate:"-" bun:"attached_mentions,rel:has-many"` // Mentions corresponding to mentionIDs - EmojiIDs []string `validate:"dive,ulid" bun:"emojis,array"` // Database IDs of any emojis used in this status - Emojis []*Emoji `validate:"-" bun:"attached_emojis,m2m:status_to_emojis"` // Emojis corresponding to emojiIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation - Local *bool `validate:"-" bun:",nullzero,notnull,default:false"` // is this status from a local account? - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // which account posted this status? - Account *Account `validate:"-" bun:"rel:belongs-to"` // account corresponding to accountID - AccountURI string `validate:"required,url" bun:",nullzero,notnull"` // activitypub uri of the owner of this status - InReplyToID string `validate:"required_with=InReplyToURI InReplyToAccountID,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the status this status replies to - InReplyToURI string `validate:"required_with=InReplyToID InReplyToAccountID,omitempty,url" bun:",nullzero"` // activitypub uri of the status this status is a reply to - InReplyToAccountID string `validate:"required_with=InReplyToID InReplyToURI,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the account that this status replies to - InReplyTo *Status `validate:"-" bun:"-"` // status corresponding to inReplyToID - InReplyToAccount *Account `validate:"-" bun:"rel:belongs-to"` // account corresponding to inReplyToAccountID - BoostOfID string `validate:"required_with=BoostOfAccountID,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the status this status is a boost of - BoostOfAccountID string `validate:"required_with=BoostOfID,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the account that owns the boosted status - BoostOf *Status `validate:"-" bun:"-"` // status that corresponds to boostOfID - BoostOfAccount *Account `validate:"-" bun:"rel:belongs-to"` // account that corresponds to boostOfAccountID - ContentWarning string `validate:"-" bun:",nullzero"` // cw string for this status - Visibility Visibility `validate:"oneof=public unlocked followers_only mutuals_only direct" bun:",nullzero,notnull"` // visibility entry for this status - Sensitive *bool `validate:"-" bun:",nullzero,notnull,default:false"` // mark the status as sensitive? - Language string `validate:"-" bun:",nullzero"` // what language is this status written in? - CreatedWithApplicationID string `validate:"required_if=Local true,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Which application was used to create this status? - CreatedWithApplication *Application `validate:"-" bun:"rel:belongs-to"` // application corresponding to createdWithApplicationID - ActivityStreamsType string `validate:"required" bun:",nullzero,notnull"` // What is the activitystreams type of this status? See: https://www.w3.org/TR/activitystreams-vocabulary/#object-types. Will probably almost always be Note but who knows!. - Text string `validate:"-" bun:""` // Original text of the status without formatting - Federated *bool `validate:"-" bun:",notnull"` // This status will be federated beyond the local timeline(s) - Boostable *bool `validate:"-" bun:",notnull"` // This status can be boosted/reblogged - Replyable *bool `validate:"-" bun:",notnull"` // This status can be replied to - Likeable *bool `validate:"-" bun:",notnull"` // This status can be liked/faved + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + FetchedAt time.Time `bun:"type:timestamptz,nullzero"` // when was item (remote) last fetched. + PinnedAt time.Time `bun:"type:timestamptz,nullzero"` // Status was pinned by owning account at this time. + URI string `bun:",unique,nullzero,notnull"` // activitypub URI of this status + URL string `bun:",nullzero"` // web url for viewing this status + Content string `bun:""` // content of this status; likely html-formatted but not guaranteed + AttachmentIDs []string `bun:"attachments,array"` // Database IDs of any media attachments associated with this status + Attachments []*MediaAttachment `bun:"attached_media,rel:has-many"` // Attachments corresponding to attachmentIDs + TagIDs []string `bun:"tags,array"` // Database IDs of any tags used in this status + Tags []*Tag `bun:"attached_tags,m2m:status_to_tags"` // Tags corresponding to tagIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation + MentionIDs []string `bun:"mentions,array"` // Database IDs of any mentions in this status + Mentions []*Mention `bun:"attached_mentions,rel:has-many"` // Mentions corresponding to mentionIDs + EmojiIDs []string `bun:"emojis,array"` // Database IDs of any emojis used in this status + Emojis []*Emoji `bun:"attached_emojis,m2m:status_to_emojis"` // Emojis corresponding to emojiIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation + Local *bool `bun:",nullzero,notnull,default:false"` // is this status from a local account? + AccountID string `bun:"type:CHAR(26),nullzero,notnull"` // which account posted this status? + Account *Account `bun:"rel:belongs-to"` // account corresponding to accountID + AccountURI string `bun:",nullzero,notnull"` // activitypub uri of the owner of this status + InReplyToID string `bun:"type:CHAR(26),nullzero"` // id of the status this status replies to + InReplyToURI string `bun:",nullzero"` // activitypub uri of the status this status is a reply to + InReplyToAccountID string `bun:"type:CHAR(26),nullzero"` // id of the account that this status replies to + InReplyTo *Status `bun:"-"` // status corresponding to inReplyToID + InReplyToAccount *Account `bun:"rel:belongs-to"` // account corresponding to inReplyToAccountID + BoostOfID string `bun:"type:CHAR(26),nullzero"` // id of the status this status is a boost of + BoostOfAccountID string `bun:"type:CHAR(26),nullzero"` // id of the account that owns the boosted status + BoostOf *Status `bun:"-"` // status that corresponds to boostOfID + BoostOfAccount *Account `bun:"rel:belongs-to"` // account that corresponds to boostOfAccountID + ContentWarning string `bun:",nullzero"` // cw string for this status + Visibility Visibility `bun:",nullzero,notnull"` // visibility entry for this status + Sensitive *bool `bun:",nullzero,notnull,default:false"` // mark the status as sensitive? + Language string `bun:",nullzero"` // what language is this status written in? + CreatedWithApplicationID string `bun:"type:CHAR(26),nullzero"` // Which application was used to create this status? + CreatedWithApplication *Application `bun:"rel:belongs-to"` // application corresponding to createdWithApplicationID + ActivityStreamsType string `bun:",nullzero,notnull"` // What is the activitystreams type of this status? See: https://www.w3.org/TR/activitystreams-vocabulary/#object-types. Will probably almost always be Note but who knows!. + Text string `bun:""` // Original text of the status without formatting + Federated *bool `bun:",notnull"` // This status will be federated beyond the local timeline(s) + Boostable *bool `bun:",notnull"` // This status can be boosted/reblogged + Replyable *bool `bun:",notnull"` // This status can be replied to + Likeable *bool `bun:",notnull"` // This status can be liked/faved } // GetID implements timeline.Timelineable{}. @@ -252,18 +252,18 @@ func (s *Status) MentionsAccount(id string) bool { // StatusToTag is an intermediate struct to facilitate the many2many relationship between a status and one or more tags. type StatusToTag struct { - StatusID string `validate:"ulid,required" bun:"type:CHAR(26),unique:statustag,nullzero,notnull"` - Status *Status `validate:"-" bun:"rel:belongs-to"` - TagID string `validate:"ulid,required" bun:"type:CHAR(26),unique:statustag,nullzero,notnull"` - Tag *Tag `validate:"-" bun:"rel:belongs-to"` + StatusID string `bun:"type:CHAR(26),unique:statustag,nullzero,notnull"` + Status *Status `bun:"rel:belongs-to"` + TagID string `bun:"type:CHAR(26),unique:statustag,nullzero,notnull"` + Tag *Tag `bun:"rel:belongs-to"` } // StatusToEmoji is an intermediate struct to facilitate the many2many relationship between a status and one or more emojis. type StatusToEmoji struct { - StatusID string `validate:"ulid,required" bun:"type:CHAR(26),unique:statusemoji,nullzero,notnull"` - Status *Status `validate:"-" bun:"rel:belongs-to"` - EmojiID string `validate:"ulid,required" bun:"type:CHAR(26),unique:statusemoji,nullzero,notnull"` - Emoji *Emoji `validate:"-" bun:"rel:belongs-to"` + StatusID string `bun:"type:CHAR(26),unique:statusemoji,nullzero,notnull"` + Status *Status `bun:"rel:belongs-to"` + EmojiID string `bun:"type:CHAR(26),unique:statusemoji,nullzero,notnull"` + Emoji *Emoji `bun:"rel:belongs-to"` } // Visibility represents the visibility granularity of a status. diff --git a/internal/gtsmodel/statusbookmark.go b/internal/gtsmodel/statusbookmark.go index 6141b6e34..c7590a5c3 100644 --- a/internal/gtsmodel/statusbookmark.go +++ b/internal/gtsmodel/statusbookmark.go @@ -21,13 +21,13 @@ import "time" // StatusBookmark refers to one account having a 'bookmark' of the status of another account. type StatusBookmark struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // id of the account that created ('did') the bookmark - Account *Account `validate:"-" bun:"rel:belongs-to"` // account that created the bookmark - TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // id the account owning the bookmarked status - TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // account owning the bookmarked status - StatusID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // database id of the status that has been bookmarked - Status *Status `validate:"-" bun:"rel:belongs-to"` // the bookmarked status + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + AccountID string `bun:"type:CHAR(26),nullzero,notnull"` // id of the account that created ('did') the bookmark + Account *Account `bun:"rel:belongs-to"` // account that created the bookmark + TargetAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // id the account owning the bookmarked status + TargetAccount *Account `bun:"rel:belongs-to"` // account owning the bookmarked status + StatusID string `bun:"type:CHAR(26),nullzero,notnull"` // database id of the status that has been bookmarked + Status *Status `bun:"rel:belongs-to"` // the bookmarked status } diff --git a/internal/gtsmodel/statusfave.go b/internal/gtsmodel/statusfave.go index 86096ef5e..f81226f8b 100644 --- a/internal/gtsmodel/statusfave.go +++ b/internal/gtsmodel/statusfave.go @@ -21,14 +21,14 @@ import "time" // StatusFave refers to a 'fave' or 'like' in the database, from one account, targeting the status of another account type StatusFave struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:statusfaveaccountstatus,nullzero,notnull"` // id of the account that created ('did') the fave - Account *Account `validate:"-" bun:"-"` // account that created the fave - TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // id the account owning the faved status - TargetAccount *Account `validate:"-" bun:"-"` // account owning the faved status - StatusID string `validate:"required,ulid" bun:"type:CHAR(26),unique:statusfaveaccountstatus,nullzero,notnull"` // database id of the status that has been 'faved' - Status *Status `validate:"-" bun:"-"` // the faved status - URI string `validate:"required,url" bun:",nullzero,notnull,unique"` // ActivityPub URI of this fave + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + AccountID string `bun:"type:CHAR(26),unique:statusfaveaccountstatus,nullzero,notnull"` // id of the account that created ('did') the fave + Account *Account `bun:"-"` // account that created the fave + TargetAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // id the account owning the faved status + TargetAccount *Account `bun:"-"` // account owning the faved status + StatusID string `bun:"type:CHAR(26),unique:statusfaveaccountstatus,nullzero,notnull"` // database id of the status that has been 'faved' + Status *Status `bun:"-"` // the faved status + URI string `bun:",nullzero,notnull,unique"` // ActivityPub URI of this fave } diff --git a/internal/gtsmodel/statusmute.go b/internal/gtsmodel/statusmute.go index 27af921e4..b8aca1c7a 100644 --- a/internal/gtsmodel/statusmute.go +++ b/internal/gtsmodel/statusmute.go @@ -21,13 +21,13 @@ import "time" // StatusMute refers to one account having muted the status of another account or its own. type StatusMute struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // id of the account that created ('did') the mute - Account *Account `validate:"-" bun:"rel:belongs-to"` // pointer to the account specified by accountID - TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // id the account owning the muted status (can be the same as accountID) - TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // pointer to the account specified by targetAccountID - StatusID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // database id of the status that has been muted - Status *Status `validate:"-" bun:"rel:belongs-to"` // pointer to the muted status specified by statusID + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + AccountID string `bun:"type:CHAR(26),nullzero,notnull"` // id of the account that created ('did') the mute + Account *Account `bun:"rel:belongs-to"` // pointer to the account specified by accountID + TargetAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // id the account owning the muted status (can be the same as accountID) + TargetAccount *Account `bun:"rel:belongs-to"` // pointer to the account specified by targetAccountID + StatusID string `bun:"type:CHAR(26),nullzero,notnull"` // database id of the status that has been muted + Status *Status `bun:"rel:belongs-to"` // pointer to the muted status specified by statusID } diff --git a/internal/gtsmodel/tag.go b/internal/gtsmodel/tag.go index a43c4a5ec..514389f23 100644 --- a/internal/gtsmodel/tag.go +++ b/internal/gtsmodel/tag.go @@ -21,10 +21,10 @@ import "time" // Tag represents a hashtag for gathering public statuses together. type Tag struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - Name string `validate:"required" bun:",unique,nullzero,notnull"` // (lowercase) name of the tag without the hash prefix - Useable *bool `validate:"-" bun:",nullzero,notnull,default:true"` // Tag is useable on this instance. - Listable *bool `validate:"-" bun:",nullzero,notnull,default:true"` // Tagged statuses can be listed on this instance. + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + Name string `bun:",unique,nullzero,notnull"` // (lowercase) name of the tag without the hash prefix + Useable *bool `bun:",nullzero,notnull,default:true"` // Tag is useable on this instance. + Listable *bool `bun:",nullzero,notnull,default:true"` // Tagged statuses can be listed on this instance. } diff --git a/internal/gtsmodel/token.go b/internal/gtsmodel/token.go index 06db7e394..fd640abde 100644 --- a/internal/gtsmodel/token.go +++ b/internal/gtsmodel/token.go @@ -21,22 +21,22 @@ import "time" // Token is a translation of the gotosocial token with the ExpiresIn fields replaced with ExpiresAt. type Token struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - ClientID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // ID of the client who owns this token - UserID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero"` // ID of the user who owns this token - RedirectURI string `validate:"required,uri" bun:",nullzero,notnull"` // Oauth redirect URI for this token - Scope string `validate:"required" bun:",notnull"` // Oauth scope - Code string `validate:"-" bun:",pk,nullzero,notnull,default:''"` // Code, if present - CodeChallenge string `validate:"-" bun:",nullzero"` // Code challenge, if code present - CodeChallengeMethod string `validate:"-" bun:",nullzero"` // Code challenge method, if code present - CodeCreateAt time.Time `validate:"required_with=Code" bun:"type:timestamptz,nullzero"` // Code created time, if code present - CodeExpiresAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // Code expires at -- null means the code never expires - Access string `validate:"-" bun:",pk,nullzero,notnull,default:''"` // User level access token, if present - AccessCreateAt time.Time `validate:"required_with=Access" bun:"type:timestamptz,nullzero"` // User level access token created time, if access present - AccessExpiresAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // User level access token expires at -- null means the token never expires - Refresh string `validate:"-" bun:",pk,nullzero,notnull,default:''"` // Refresh token, if present - RefreshCreateAt time.Time `validate:"required_with=Refresh" bun:"type:timestamptz,nullzero"` // Refresh created at, if refresh present - RefreshExpiresAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // Refresh expires at -- null means the refresh token never expires + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + ClientID string `bun:"type:CHAR(26),nullzero,notnull"` // ID of the client who owns this token + UserID string `bun:"type:CHAR(26),nullzero"` // ID of the user who owns this token + RedirectURI string `bun:",nullzero,notnull"` // Oauth redirect URI for this token + Scope string `bun:",notnull"` // Oauth scope + Code string `bun:",pk,nullzero,notnull,default:''"` // Code, if present + CodeChallenge string `bun:",nullzero"` // Code challenge, if code present + CodeChallengeMethod string `bun:",nullzero"` // Code challenge method, if code present + CodeCreateAt time.Time `bun:"type:timestamptz,nullzero"` // Code created time, if code present + CodeExpiresAt time.Time `bun:"type:timestamptz,nullzero"` // Code expires at -- null means the code never expires + Access string `bun:",pk,nullzero,notnull,default:''"` // User level access token, if present + AccessCreateAt time.Time `bun:"type:timestamptz,nullzero"` // User level access token created time, if access present + AccessExpiresAt time.Time `bun:"type:timestamptz,nullzero"` // User level access token expires at -- null means the token never expires + Refresh string `bun:",pk,nullzero,notnull,default:''"` // Refresh token, if present + RefreshCreateAt time.Time `bun:"type:timestamptz,nullzero"` // Refresh created at, if refresh present + RefreshExpiresAt time.Time `bun:"type:timestamptz,nullzero"` // Refresh expires at -- null means the refresh token never expires } diff --git a/internal/gtsmodel/tombstone.go b/internal/gtsmodel/tombstone.go index f7e1c2504..7c6af6de3 100644 --- a/internal/gtsmodel/tombstone.go +++ b/internal/gtsmodel/tombstone.go @@ -29,9 +29,9 @@ import ( // It's useful in cases where a remote account has been deleted, and we don't want to keep trying to process // subsequent activities from that account, or deletes which target it. type Tombstone struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - Domain string `validate:"omitempty,fqdn" bun:",nullzero,notnull"` // Domain of the Object/Actor. - URI string `validate:"required,url" bun:",nullzero,notnull,unique"` // ActivityPub URI for this Object/Actor. + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + Domain string `bun:",nullzero,notnull"` // Domain of the Object/Actor. + URI string `bun:",nullzero,notnull,unique"` // ActivityPub URI for this Object/Actor. } diff --git a/internal/gtsmodel/user.go b/internal/gtsmodel/user.go index 2f2029636..16c4d4ee2 100644 --- a/internal/gtsmodel/user.go +++ b/internal/gtsmodel/user.go @@ -25,35 +25,35 @@ import ( // User represents an actual human user of gotosocial. Note, this is a LOCAL gotosocial user, not a remote account. // To cross reference this local user with their account (which can be local or remote), use the AccountID field. type User struct { - ID string `validate:"required,ulid" bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database - CreatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated - Email string `validate:"required_with=ConfirmedAt" bun:",nullzero,unique"` // confirmed email address for this user, this should be unique -- only one email address registered per instance, multiple users per email are not supported - AccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull,unique"` // The id of the local gtsmodel.Account entry for this user. - Account *Account `validate:"-" bun:"rel:belongs-to"` // Pointer to the account of this user that corresponds to AccountID. - EncryptedPassword string `validate:"required" bun:",nullzero,notnull"` // The encrypted password of this user, generated using https://pkg.go.dev/golang.org/x/crypto/bcrypt#GenerateFromPassword. A salt is included so we're safe against 🌈 tables. - SignUpIP net.IP `validate:"-" bun:",nullzero"` // From what IP was this user created? - CurrentSignInAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When did the user sign in with their current session. - CurrentSignInIP net.IP `validate:"-" bun:",nullzero"` // What's the most recent IP of this user - LastSignInAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When did this user last sign in? - LastSignInIP net.IP `validate:"-" bun:",nullzero"` // What's the previous IP of this user? - SignInCount int `validate:"min=0" bun:",notnull,default:0"` // How many times has this user signed in? - InviteID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the user who invited this user (who let this joker in?) - ChosenLanguages []string `validate:"-" bun:",nullzero"` // What languages does this user want to see? - FilteredLanguages []string `validate:"-" bun:",nullzero"` // What languages does this user not want to see? - Locale string `validate:"-" bun:",nullzero"` // In what timezone/locale is this user located? - CreatedByApplicationID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Which application id created this user? See gtsmodel.Application - CreatedByApplication *Application `validate:"-" bun:"rel:belongs-to"` // Pointer to the application corresponding to createdbyapplicationID. - LastEmailedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When was this user last contacted by email. - ConfirmationToken string `validate:"required_with=ConfirmationSentAt" bun:",nullzero"` // What confirmation token did we send this user/what are we expecting back? - ConfirmationSentAt time.Time `validate:"required_with=ConfirmationToken" bun:"type:timestamptz,nullzero"` // When did we send email confirmation to this user? - ConfirmedAt time.Time `validate:"required_with=Email" bun:"type:timestamptz,nullzero"` // When did the user confirm their email address - UnconfirmedEmail string `validate:"required_without=Email" bun:",nullzero"` // Email address that hasn't yet been confirmed - Moderator *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this user a moderator? - Admin *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this user an admin? - Disabled *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this user disabled from posting? - Approved *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Has this user been approved by a moderator? - ResetPasswordToken string `validate:"required_with=ResetPasswordSentAt" bun:",nullzero"` // The generated token that the user can use to reset their password - ResetPasswordSentAt time.Time `validate:"required_with=ResetPasswordToken" bun:"type:timestamptz,nullzero"` // When did we email the user their reset-password email? - ExternalID string `validate:"-" bun:",nullzero,unique"` // If the login for the user is managed externally (e.g OIDC), we need to keep a stable reference to the external object (e.g OIDC sub claim) + ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database + CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated + Email string `bun:",nullzero,unique"` // confirmed email address for this user, this should be unique -- only one email address registered per instance, multiple users per email are not supported + AccountID string `bun:"type:CHAR(26),nullzero,notnull,unique"` // The id of the local gtsmodel.Account entry for this user. + Account *Account `bun:"rel:belongs-to"` // Pointer to the account of this user that corresponds to AccountID. + EncryptedPassword string `bun:",nullzero,notnull"` // The encrypted password of this user, generated using https://pkg.go.dev/golang.org/x/crypto/bcrypt#GenerateFromPassword. A salt is included so we're safe against 🌈 tables. + SignUpIP net.IP `bun:",nullzero"` // From what IP was this user created? + CurrentSignInAt time.Time `bun:"type:timestamptz,nullzero"` // When did the user sign in with their current session. + CurrentSignInIP net.IP `bun:",nullzero"` // What's the most recent IP of this user + LastSignInAt time.Time `bun:"type:timestamptz,nullzero"` // When did this user last sign in? + LastSignInIP net.IP `bun:",nullzero"` // What's the previous IP of this user? + SignInCount int `bun:",notnull,default:0"` // How many times has this user signed in? + InviteID string `bun:"type:CHAR(26),nullzero"` // id of the user who invited this user (who let this joker in?) + ChosenLanguages []string `bun:",nullzero"` // What languages does this user want to see? + FilteredLanguages []string `bun:",nullzero"` // What languages does this user not want to see? + Locale string `bun:",nullzero"` // In what timezone/locale is this user located? + CreatedByApplicationID string `bun:"type:CHAR(26),nullzero"` // Which application id created this user? See gtsmodel.Application + CreatedByApplication *Application `bun:"rel:belongs-to"` // Pointer to the application corresponding to createdbyapplicationID. + LastEmailedAt time.Time `bun:"type:timestamptz,nullzero"` // When was this user last contacted by email. + ConfirmationToken string `bun:",nullzero"` // What confirmation token did we send this user/what are we expecting back? + ConfirmationSentAt time.Time `bun:"type:timestamptz,nullzero"` // When did we send email confirmation to this user? + ConfirmedAt time.Time `bun:"type:timestamptz,nullzero"` // When did the user confirm their email address + UnconfirmedEmail string `bun:",nullzero"` // Email address that hasn't yet been confirmed + Moderator *bool `bun:",nullzero,notnull,default:false"` // Is this user a moderator? + Admin *bool `bun:",nullzero,notnull,default:false"` // Is this user an admin? + Disabled *bool `bun:",nullzero,notnull,default:false"` // Is this user disabled from posting? + Approved *bool `bun:",nullzero,notnull,default:false"` // Has this user been approved by a moderator? + ResetPasswordToken string `bun:",nullzero"` // The generated token that the user can use to reset their password + ResetPasswordSentAt time.Time `bun:"type:timestamptz,nullzero"` // When did we email the user their reset-password email? + ExternalID string `bun:",nullzero,unique"` // If the login for the user is managed externally (e.g OIDC), we need to keep a stable reference to the external object (e.g OIDC sub claim) } diff --git a/internal/validate/account_test.go b/internal/validate/account_test.go deleted file mode 100644 index 57f0f4900..000000000 --- a/internal/validate/account_test.go +++ /dev/null @@ -1,343 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "crypto/rand" - "crypto/rsa" - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/ap" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" - "github.com/superseriousbusiness/gotosocial/testrig" -) - -func happyAccount() *gtsmodel.Account { - priv, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - panic(err) - } - pub := &priv.PublicKey - - return >smodel.Account{ - ID: "01F8MH1H7YV1Z7D2C8K2730QBF", - CreatedAt: time.Now().Add(-48 * time.Hour), - UpdatedAt: time.Now().Add(-48 * time.Hour), - Username: "the_mighty_zork", - Domain: "", - AvatarMediaAttachmentID: "01F8MH58A357CV5K7R7TJMSH6S", - AvatarMediaAttachment: nil, - AvatarRemoteURL: "", - HeaderMediaAttachmentID: "01PFPMWK2FF0D9WMHEJHR07C3Q", - HeaderMediaAttachment: nil, - HeaderRemoteURL: "", - DisplayName: "original zork (he/they)", - Fields: []*gtsmodel.Field{}, - Note: "hey yo this is my profile!", - Memorial: testrig.FalseBool(), - AlsoKnownAs: "", - MovedToAccountID: "", - Bot: testrig.FalseBool(), - Reason: "I wanna be on this damned webbed site so bad! Please! Wow", - Locked: testrig.FalseBool(), - Discoverable: testrig.TrueBool(), - Privacy: gtsmodel.VisibilityPublic, - Sensitive: testrig.FalseBool(), - Language: "en", - StatusContentType: "text/plain", - URI: "http://localhost:8080/users/the_mighty_zork", - URL: "http://localhost:8080/@the_mighty_zork", - FetchedAt: time.Time{}, - InboxURI: "http://localhost:8080/users/the_mighty_zork/inbox", - OutboxURI: "http://localhost:8080/users/the_mighty_zork/outbox", - FollowersURI: "http://localhost:8080/users/the_mighty_zork/followers", - FollowingURI: "http://localhost:8080/users/the_mighty_zork/following", - FeaturedCollectionURI: "http://localhost:8080/users/the_mighty_zork/collections/featured", - ActorType: ap.ActorPerson, - PrivateKey: priv, - PublicKey: pub, - PublicKeyURI: "http://localhost:8080/users/the_mighty_zork#main-key", - SensitizedAt: time.Time{}, - SilencedAt: time.Time{}, - SuspendedAt: time.Time{}, - HideCollections: testrig.FalseBool(), - SuspensionOrigin: "", - } -} - -type AccountValidateTestSuite struct { - suite.Suite -} - -func (suite *AccountValidateTestSuite) TestValidateAccountHappyPath() { - // no problem here - a := happyAccount() - err := validate.Struct(*a) - suite.NoError(err) -} - -// ID must be set and be valid ULID -func (suite *AccountValidateTestSuite) TestValidateAccountBadID() { - a := happyAccount() - - a.ID = "" - err := validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - a.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -// CreatedAt can be set or not -- it will be set in the database anyway -func (suite *AccountValidateTestSuite) TestValidateAccountNoCreatedAt() { - a := happyAccount() - - a.CreatedAt = time.Time{} - err := validate.Struct(*a) - suite.NoError(err) -} - -// FetchedAt must be defined if remote account -func (suite *AccountValidateTestSuite) TestValidateAccountNoWebfingeredAt() { - a := happyAccount() - - a.Domain = "example.org" - a.FetchedAt = time.Time{} - err := validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.FetchedAt' Error:Field validation for 'FetchedAt' failed on the 'required_with' tag") -} - -// Username must be set -func (suite *AccountValidateTestSuite) TestValidateAccountUsername() { - a := happyAccount() - - a.Username = "" - err := validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.Username' Error:Field validation for 'Username' failed on the 'required' tag") -} - -// Domain must be either empty (for local accounts) or proper fqdn (for remote accounts) -func (suite *AccountValidateTestSuite) TestValidateAccountDomain() { - a := happyAccount() - a.FetchedAt = time.Now() - - a.Domain = "" - err := validate.Struct(*a) - suite.NoError(err) - - a.Domain = "localhost:8080" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag") - - a.Domain = "ahhhhh" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag") - - a.Domain = "https://www.example.org" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag") - - a.Domain = "example.org:8080" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag") - - a.Domain = "example.org" - err = validate.Struct(*a) - suite.NoError(err) -} - -// Attachment IDs must either be not set, or must be valid ULID -func (suite *AccountValidateTestSuite) TestValidateAttachmentIDs() { - a := happyAccount() - - a.AvatarMediaAttachmentID = "" - a.HeaderMediaAttachmentID = "" - err := validate.Struct(*a) - suite.NoError(err) - - a.AvatarMediaAttachmentID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - a.HeaderMediaAttachmentID = "aaaa" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.AvatarMediaAttachmentID' Error:Field validation for 'AvatarMediaAttachmentID' failed on the 'ulid' tag\nKey: 'Account.HeaderMediaAttachmentID' Error:Field validation for 'HeaderMediaAttachmentID' failed on the 'ulid' tag") -} - -// Attachment remote URLs must either not be set, or be valid URLs -func (suite *AccountValidateTestSuite) TestValidateAttachmentRemoteURLs() { - a := happyAccount() - - a.AvatarRemoteURL = "" - a.HeaderRemoteURL = "" - err := validate.Struct(*a) - suite.NoError(err) - - a.AvatarRemoteURL = "-------------" - a.HeaderRemoteURL = "https://valid-url.com" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.AvatarRemoteURL' Error:Field validation for 'AvatarRemoteURL' failed on the 'url' tag") - - a.AvatarRemoteURL = "https://valid-url.com" - a.HeaderRemoteURL = "" - err = validate.Struct(*a) - suite.NoError(err) -} - -// Default privacy must be set if account is local -func (suite *AccountValidateTestSuite) TestValidatePrivacy() { - a := happyAccount() - a.FetchedAt = time.Now() - - a.Privacy = "" - err := validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.Privacy' Error:Field validation for 'Privacy' failed on the 'required_without' tag") - - a.Privacy = "not valid" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.Privacy' Error:Field validation for 'Privacy' failed on the 'oneof' tag") - - a.Privacy = gtsmodel.VisibilityFollowersOnly - err = validate.Struct(*a) - suite.NoError(err) - - a.Privacy = "" - a.Domain = "example.org" - err = validate.Struct(*a) - suite.NoError(err) - - a.Privacy = "invalid" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.Privacy' Error:Field validation for 'Privacy' failed on the 'oneof' tag") -} - -// If set, language must be a valid language -func (suite *AccountValidateTestSuite) TestValidateLanguage() { - a := happyAccount() - - a.Language = "" - err := validate.Struct(*a) - suite.NoError(err) - - a.Language = "not valid" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.Language' Error:Field validation for 'Language' failed on the 'bcp47_language_tag' tag") - - a.Language = "en-uk" - err = validate.Struct(*a) - suite.NoError(err) -} - -// Account URI must be set and must be valid -func (suite *AccountValidateTestSuite) TestValidateAccountURI() { - a := happyAccount() - - a.URI = "invalid-uri" - err := validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.URI' Error:Field validation for 'URI' failed on the 'url' tag") - - a.URI = "" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.URI' Error:Field validation for 'URI' failed on the 'required' tag") -} - -// ActivityPub URIs must be set on account if it's local -func (suite *AccountValidateTestSuite) TestValidateAccountURIs() { - a := happyAccount() - a.FetchedAt = time.Now() - - a.InboxURI = "invalid-uri" - a.OutboxURI = "invalid-uri" - a.FollowersURI = "invalid-uri" - a.FollowingURI = "invalid-uri" - a.FeaturedCollectionURI = "invalid-uri" - a.PublicKeyURI = "invalid-uri" - err := validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.InboxURI' Error:Field validation for 'InboxURI' failed on the 'url' tag\nKey: 'Account.OutboxURI' Error:Field validation for 'OutboxURI' failed on the 'url' tag\nKey: 'Account.FollowingURI' Error:Field validation for 'FollowingURI' failed on the 'url' tag\nKey: 'Account.FollowersURI' Error:Field validation for 'FollowersURI' failed on the 'url' tag\nKey: 'Account.FeaturedCollectionURI' Error:Field validation for 'FeaturedCollectionURI' failed on the 'url' tag\nKey: 'Account.PublicKeyURI' Error:Field validation for 'PublicKeyURI' failed on the 'url' tag") - - a.InboxURI = "" - a.OutboxURI = "" - a.FollowersURI = "" - a.FollowingURI = "" - a.FeaturedCollectionURI = "" - a.PublicKeyURI = "" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.InboxURI' Error:Field validation for 'InboxURI' failed on the 'required_without' tag\nKey: 'Account.OutboxURI' Error:Field validation for 'OutboxURI' failed on the 'required_without' tag\nKey: 'Account.FollowingURI' Error:Field validation for 'FollowingURI' failed on the 'required_without' tag\nKey: 'Account.FollowersURI' Error:Field validation for 'FollowersURI' failed on the 'required_without' tag\nKey: 'Account.FeaturedCollectionURI' Error:Field validation for 'FeaturedCollectionURI' failed on the 'required_without' tag\nKey: 'Account.PublicKeyURI' Error:Field validation for 'PublicKeyURI' failed on the 'required' tag") - - a.Domain = "example.org" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.PublicKeyURI' Error:Field validation for 'PublicKeyURI' failed on the 'required' tag") - - a.InboxURI = "invalid-uri" - a.OutboxURI = "invalid-uri" - a.FollowersURI = "invalid-uri" - a.FollowingURI = "invalid-uri" - a.FeaturedCollectionURI = "invalid-uri" - a.PublicKeyURI = "invalid-uri" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.InboxURI' Error:Field validation for 'InboxURI' failed on the 'url' tag\nKey: 'Account.OutboxURI' Error:Field validation for 'OutboxURI' failed on the 'url' tag\nKey: 'Account.FollowingURI' Error:Field validation for 'FollowingURI' failed on the 'url' tag\nKey: 'Account.FollowersURI' Error:Field validation for 'FollowersURI' failed on the 'url' tag\nKey: 'Account.FeaturedCollectionURI' Error:Field validation for 'FeaturedCollectionURI' failed on the 'url' tag\nKey: 'Account.PublicKeyURI' Error:Field validation for 'PublicKeyURI' failed on the 'url' tag") -} - -// Actor type must be set and valid -func (suite *AccountValidateTestSuite) TestValidateActorType() { - a := happyAccount() - - a.ActorType = "" - err := validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.ActorType' Error:Field validation for 'ActorType' failed on the 'oneof' tag") - - a.ActorType = "not valid" - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.ActorType' Error:Field validation for 'ActorType' failed on the 'oneof' tag") - - a.ActorType = ap.ActivityArrive - err = validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.ActorType' Error:Field validation for 'ActorType' failed on the 'oneof' tag") - - a.ActorType = ap.ActorOrganization - err = validate.Struct(*a) - suite.NoError(err) -} - -// Private key must be set on local accounts -func (suite *AccountValidateTestSuite) TestValidatePrivateKey() { - a := happyAccount() - a.FetchedAt = time.Now() - - a.PrivateKey = nil - err := validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.PrivateKey' Error:Field validation for 'PrivateKey' failed on the 'required_without' tag") - - a.Domain = "example.org" - err = validate.Struct(*a) - suite.NoError(err) -} - -// Public key must be set -func (suite *AccountValidateTestSuite) TestValidatePublicKey() { - a := happyAccount() - - a.PublicKey = nil - err := validate.Struct(*a) - suite.EqualError(err, "Key: 'Account.PublicKey' Error:Field validation for 'PublicKey' failed on the 'required' tag") -} - -func TestAccountValidateTestSuite(t *testing.T) { - suite.Run(t, new(AccountValidateTestSuite)) -} diff --git a/internal/validate/application_test.go b/internal/validate/application_test.go deleted file mode 100644 index 86c53a615..000000000 --- a/internal/validate/application_test.go +++ /dev/null @@ -1,132 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyApplication() *gtsmodel.Application { - return >smodel.Application{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - Name: "Tusky", - Website: "https://tusky.app", - RedirectURI: "oauth2redirect://com.keylesspalace.tusky/", - ClientID: "01FEEDMF6C0QD589MRK7919Z0R", - ClientSecret: "bd740cf1-024a-4e4d-8c39-866538f52fe6", - Scopes: "read write follow", - } -} - -type ApplicationValidateTestSuite struct { - suite.Suite -} - -func (suite *ApplicationValidateTestSuite) TestValidateApplicationHappyPath() { - // no problem here - a := happyApplication() - err := validate.Struct(a) - suite.NoError(err) -} - -func (suite *ApplicationValidateTestSuite) TestValidateApplicationBadID() { - a := happyApplication() - - a.ID = "" - err := validate.Struct(a) - suite.EqualError(err, "Key: 'Application.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - a.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(a) - suite.EqualError(err, "Key: 'Application.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *ApplicationValidateTestSuite) TestValidateApplicationNoCreatedAt() { - a := happyApplication() - - a.CreatedAt = time.Time{} - err := validate.Struct(a) - suite.NoError(err) -} - -func (suite *ApplicationValidateTestSuite) TestValidateApplicationName() { - a := happyApplication() - - a.Name = "" - err := validate.Struct(a) - suite.EqualError(err, "Key: 'Application.Name' Error:Field validation for 'Name' failed on the 'required' tag") -} - -func (suite *ApplicationValidateTestSuite) TestValidateApplicationWebsite() { - a := happyApplication() - - a.Website = "invalid-website" - err := validate.Struct(a) - suite.EqualError(err, "Key: 'Application.Website' Error:Field validation for 'Website' failed on the 'url' tag") - - a.Website = "" - err = validate.Struct(a) - suite.NoError(err) -} - -func (suite *ApplicationValidateTestSuite) TestValidateApplicationRedirectURI() { - a := happyApplication() - - a.RedirectURI = "invalid-uri" - err := validate.Struct(a) - suite.EqualError(err, "Key: 'Application.RedirectURI' Error:Field validation for 'RedirectURI' failed on the 'uri' tag") - - a.RedirectURI = "" - err = validate.Struct(a) - suite.EqualError(err, "Key: 'Application.RedirectURI' Error:Field validation for 'RedirectURI' failed on the 'required' tag") - - a.RedirectURI = "urn:ietf:wg:oauth:2.0:oob" - err = validate.Struct(a) - suite.NoError(err) -} - -func (suite *ApplicationValidateTestSuite) TestValidateApplicationClientSecret() { - a := happyApplication() - - a.ClientSecret = "invalid-uuid" - err := validate.Struct(a) - suite.EqualError(err, "Key: 'Application.ClientSecret' Error:Field validation for 'ClientSecret' failed on the 'uuid' tag") - - a.ClientSecret = "" - err = validate.Struct(a) - suite.EqualError(err, "Key: 'Application.ClientSecret' Error:Field validation for 'ClientSecret' failed on the 'required' tag") -} - -func (suite *ApplicationValidateTestSuite) TestValidateApplicationScopes() { - a := happyApplication() - - a.Scopes = "" - err := validate.Struct(a) - suite.EqualError(err, "Key: 'Application.Scopes' Error:Field validation for 'Scopes' failed on the 'required' tag") -} - -func TestApplicationValidateTestSuite(t *testing.T) { - suite.Run(t, new(ApplicationValidateTestSuite)) -} diff --git a/internal/validate/block_test.go b/internal/validate/block_test.go deleted file mode 100644 index 96e206140..000000000 --- a/internal/validate/block_test.go +++ /dev/null @@ -1,115 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyBlock() *gtsmodel.Block { - return >smodel.Block{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - URI: "https://example.org/accounts/someone/blocks/01FE91RJR88PSEEE30EV35QR8N", - AccountID: "01FEED79PRMVWPRMFHFQM8MJQN", - Account: nil, - TargetAccountID: "01FEEDMF6C0QD589MRK7919Z0R", - TargetAccount: nil, - } -} - -type BlockValidateTestSuite struct { - suite.Suite -} - -func (suite *BlockValidateTestSuite) TestValidateBlockHappyPath() { - // no problem here - b := happyBlock() - err := validate.Struct(b) - suite.NoError(err) -} - -func (suite *BlockValidateTestSuite) TestValidateBlockBadID() { - b := happyBlock() - - b.ID = "" - err := validate.Struct(b) - suite.EqualError(err, "Key: 'Block.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - b.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(b) - suite.EqualError(err, "Key: 'Block.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *BlockValidateTestSuite) TestValidateBlockNoCreatedAt() { - b := happyBlock() - - b.CreatedAt = time.Time{} - err := validate.Struct(b) - suite.NoError(err) -} - -func (suite *BlockValidateTestSuite) TestValidateBlockCreatedByAccountID() { - b := happyBlock() - - b.AccountID = "" - err := validate.Struct(b) - suite.EqualError(err, "Key: 'Block.AccountID' Error:Field validation for 'AccountID' failed on the 'required' tag") - - b.AccountID = "this-is-not-a-valid-ulid" - err = validate.Struct(b) - suite.EqualError(err, "Key: 'Block.AccountID' Error:Field validation for 'AccountID' failed on the 'ulid' tag") -} - -func (suite *BlockValidateTestSuite) TestValidateBlockTargetAccountID() { - b := happyBlock() - - b.TargetAccountID = "invalid-ulid" - err := validate.Struct(b) - suite.EqualError(err, "Key: 'Block.TargetAccountID' Error:Field validation for 'TargetAccountID' failed on the 'ulid' tag") - - b.TargetAccountID = "01FEEDHX4G7EGHF5GD9E82Y51Q" - err = validate.Struct(b) - suite.NoError(err) - - b.TargetAccountID = "" - err = validate.Struct(b) - suite.EqualError(err, "Key: 'Block.TargetAccountID' Error:Field validation for 'TargetAccountID' failed on the 'required' tag") -} - -func (suite *BlockValidateTestSuite) TestValidateBlockURI() { - b := happyBlock() - - b.URI = "invalid-uri" - err := validate.Struct(b) - suite.EqualError(err, "Key: 'Block.URI' Error:Field validation for 'URI' failed on the 'url' tag") - - b.URI = "" - err = validate.Struct(b) - suite.EqualError(err, "Key: 'Block.URI' Error:Field validation for 'URI' failed on the 'required' tag") -} - -func TestBlockValidateTestSuite(t *testing.T) { - suite.Run(t, new(BlockValidateTestSuite)) -} diff --git a/internal/validate/client_test.go b/internal/validate/client_test.go deleted file mode 100644 index ee8f0e66e..000000000 --- a/internal/validate/client_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyClient() *gtsmodel.Client { - return >smodel.Client{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - Secret: "bd740cf1-024a-4e4d-8c39-866538f52fe6", - Domain: "oauth2redirect://com.keylesspalace.tusky/", - UserID: "01FEEDMF6C0QD589MRK7919Z0R", - } -} - -type ClientValidateTestSuite struct { - suite.Suite -} - -func (suite *ClientValidateTestSuite) TestValidateClientHappyPath() { - // no problem here - c := happyClient() - err := validate.Struct(c) - suite.NoError(err) -} - -func (suite *ClientValidateTestSuite) TestValidateClientBadID() { - c := happyClient() - - c.ID = "" - err := validate.Struct(c) - suite.EqualError(err, "Key: 'Client.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - c.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(c) - suite.EqualError(err, "Key: 'Client.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *ClientValidateTestSuite) TestValidateClientNoCreatedAt() { - c := happyClient() - - c.CreatedAt = time.Time{} - err := validate.Struct(c) - suite.NoError(err) -} - -func (suite *ClientValidateTestSuite) TestValidateClientDomain() { - c := happyClient() - - c.Domain = "invalid-uri" - err := validate.Struct(c) - suite.EqualError(err, "Key: 'Client.Domain' Error:Field validation for 'Domain' failed on the 'uri' tag") - - c.Domain = "" - err = validate.Struct(c) - suite.EqualError(err, "Key: 'Client.Domain' Error:Field validation for 'Domain' failed on the 'required' tag") - - c.Domain = "urn:ietf:wg:oauth:2.0:oob" - err = validate.Struct(c) - suite.NoError(err) -} - -func (suite *ClientValidateTestSuite) TestValidateSecret() { - c := happyClient() - - c.Secret = "invalid-uuid" - err := validate.Struct(c) - suite.EqualError(err, "Key: 'Client.Secret' Error:Field validation for 'Secret' failed on the 'uuid' tag") - - c.Secret = "" - err = validate.Struct(c) - suite.EqualError(err, "Key: 'Client.Secret' Error:Field validation for 'Secret' failed on the 'required' tag") -} - -func TestClientValidateTestSuite(t *testing.T) { - suite.Run(t, new(ClientValidateTestSuite)) -} diff --git a/internal/validate/domainblock_test.go b/internal/validate/domainblock_test.go deleted file mode 100644 index f6504d1f3..000000000 --- a/internal/validate/domainblock_test.go +++ /dev/null @@ -1,122 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" - "github.com/superseriousbusiness/gotosocial/testrig" -) - -func happyDomainBlock() *gtsmodel.DomainBlock { - return >smodel.DomainBlock{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - Domain: "baddudes.suck", - CreatedByAccountID: "01FEED79PRMVWPRMFHFQM8MJQN", - PrivateComment: "we don't like em", - PublicComment: "poo poo dudes", - Obfuscate: testrig.FalseBool(), - SubscriptionID: "", - } -} - -type DomainBlockValidateTestSuite struct { - suite.Suite -} - -func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockHappyPath() { - // no problem here - d := happyDomainBlock() - err := validate.Struct(d) - suite.NoError(err) -} - -func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockBadID() { - d := happyDomainBlock() - - d.ID = "" - err := validate.Struct(d) - suite.EqualError(err, "Key: 'DomainBlock.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - d.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(d) - suite.EqualError(err, "Key: 'DomainBlock.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockNoCreatedAt() { - d := happyDomainBlock() - - d.CreatedAt = time.Time{} - err := validate.Struct(d) - suite.NoError(err) -} - -func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockBadDomain() { - d := happyDomainBlock() - - d.Domain = "" - err := validate.Struct(d) - suite.EqualError(err, "Key: 'DomainBlock.Domain' Error:Field validation for 'Domain' failed on the 'required' tag") - - d.Domain = "this-is-not-a-valid-domain" - err = validate.Struct(d) - suite.EqualError(err, "Key: 'DomainBlock.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag") -} - -func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockCreatedByAccountID() { - d := happyDomainBlock() - - d.CreatedByAccountID = "" - err := validate.Struct(d) - suite.EqualError(err, "Key: 'DomainBlock.CreatedByAccountID' Error:Field validation for 'CreatedByAccountID' failed on the 'required' tag") - - d.CreatedByAccountID = "this-is-not-a-valid-ulid" - err = validate.Struct(d) - suite.EqualError(err, "Key: 'DomainBlock.CreatedByAccountID' Error:Field validation for 'CreatedByAccountID' failed on the 'ulid' tag") -} - -func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockComments() { - d := happyDomainBlock() - - d.PrivateComment = "" - d.PublicComment = "" - err := validate.Struct(d) - suite.NoError(err) -} - -func (suite *DomainBlockValidateTestSuite) TestValidateDomainSubscriptionID() { - d := happyDomainBlock() - - d.SubscriptionID = "invalid-ulid" - err := validate.Struct(d) - suite.EqualError(err, "Key: 'DomainBlock.SubscriptionID' Error:Field validation for 'SubscriptionID' failed on the 'ulid' tag") - - d.SubscriptionID = "01FEEDHX4G7EGHF5GD9E82Y51Q" - err = validate.Struct(d) - suite.NoError(err) -} - -func TestDomainBlockValidateTestSuite(t *testing.T) { - suite.Run(t, new(DomainBlockValidateTestSuite)) -} diff --git a/internal/validate/emaildomainblock_test.go b/internal/validate/emaildomainblock_test.go deleted file mode 100644 index 2aac0d72c..000000000 --- a/internal/validate/emaildomainblock_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyEmailDomainBlock() *gtsmodel.EmailDomainBlock { - return >smodel.EmailDomainBlock{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - Domain: "baddudes.suck", - CreatedByAccountID: "01FEED79PRMVWPRMFHFQM8MJQN", - } -} - -type EmailDomainBlockValidateTestSuite struct { - suite.Suite -} - -func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockHappyPath() { - // no problem here - e := happyEmailDomainBlock() - err := validate.Struct(e) - suite.NoError(err) -} - -func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockBadID() { - e := happyEmailDomainBlock() - - e.ID = "" - err := validate.Struct(e) - suite.EqualError(err, "Key: 'EmailDomainBlock.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - e.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(e) - suite.EqualError(err, "Key: 'EmailDomainBlock.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockNoCreatedAt() { - e := happyEmailDomainBlock() - - e.CreatedAt = time.Time{} - err := validate.Struct(e) - suite.NoError(err) -} - -func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockBadDomain() { - e := happyEmailDomainBlock() - - e.Domain = "" - err := validate.Struct(e) - suite.EqualError(err, "Key: 'EmailDomainBlock.Domain' Error:Field validation for 'Domain' failed on the 'required' tag") - - e.Domain = "this-is-not-a-valid-domain" - err = validate.Struct(e) - suite.EqualError(err, "Key: 'EmailDomainBlock.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag") -} - -func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockCreatedByAccountID() { - e := happyEmailDomainBlock() - - e.CreatedByAccountID = "" - err := validate.Struct(e) - suite.EqualError(err, "Key: 'EmailDomainBlock.CreatedByAccountID' Error:Field validation for 'CreatedByAccountID' failed on the 'required' tag") - - e.CreatedByAccountID = "this-is-not-a-valid-ulid" - err = validate.Struct(e) - suite.EqualError(err, "Key: 'EmailDomainBlock.CreatedByAccountID' Error:Field validation for 'CreatedByAccountID' failed on the 'ulid' tag") -} - -func TestEmailDomainBlockValidateTestSuite(t *testing.T) { - suite.Run(t, new(EmailDomainBlockValidateTestSuite)) -} diff --git a/internal/validate/emoji_test.go b/internal/validate/emoji_test.go deleted file mode 100644 index 9192cb1fc..000000000 --- a/internal/validate/emoji_test.go +++ /dev/null @@ -1,195 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "os" - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" - "github.com/superseriousbusiness/gotosocial/testrig" -) - -func happyEmoji() *gtsmodel.Emoji { - // the file validator actually runs os.Stat on given paths, so we need to just create small - // temp files for both the main attachment file and the thumbnail - - imageFile, err := os.CreateTemp("", "gts_test_emoji") - if err != nil { - panic(err) - } - if _, err := imageFile.WriteString("main"); err != nil { - panic(err) - } - imagePath := imageFile.Name() - if err := imageFile.Close(); err != nil { - panic(err) - } - - staticFile, err := os.CreateTemp("", "gts_test_emoji_static") - if err != nil { - panic(err) - } - if _, err := staticFile.WriteString("thumbnail"); err != nil { - panic(err) - } - imageStaticPath := staticFile.Name() - if err := staticFile.Close(); err != nil { - panic(err) - } - - return >smodel.Emoji{ - ID: "01F8MH6NEM8D7527KZAECTCR76", - CreatedAt: time.Now().Add(-71 * time.Hour), - UpdatedAt: time.Now().Add(-71 * time.Hour), - Shortcode: "blob_test", - Domain: "example.org", - ImageRemoteURL: "https://example.org/emojis/blob_test.gif", - ImageStaticRemoteURL: "https://example.org/emojis/blob_test.png", - ImageURL: "", - ImageStaticURL: "", - ImagePath: imagePath, - ImageStaticPath: imageStaticPath, - ImageContentType: "image/gif", - ImageStaticContentType: "image/png", - ImageFileSize: 1024, - ImageStaticFileSize: 256, - ImageUpdatedAt: time.Now(), - Disabled: testrig.FalseBool(), - URI: "https://example.org/emojis/blob_test", - VisibleInPicker: testrig.TrueBool(), - CategoryID: "01FEE47ZH70PWDSEAVBRFNX325", - } -} - -type EmojiValidateTestSuite struct { - suite.Suite -} - -func (suite *EmojiValidateTestSuite) TestValidateEmojiHappyPath() { - // no problem here - m := happyEmoji() - err := validate.Struct(*m) - suite.NoError(err) -} - -func (suite *EmojiValidateTestSuite) TestValidateEmojiBadFilePaths() { - e := happyEmoji() - - e.ImagePath = "/tmp/nonexistent/file/for/gotosocial/test" - err := validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImagePath' Error:Field validation for 'ImagePath' failed on the 'file' tag") - - e.ImagePath = "" - err = validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImagePath' Error:Field validation for 'ImagePath' failed on the 'required' tag") - - e.ImagePath = "???????????thisnot a valid path####" - err = validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImagePath' Error:Field validation for 'ImagePath' failed on the 'file' tag") - - e.ImageStaticPath = "/tmp/nonexistent/file/for/gotosocial/test" - err = validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImagePath' Error:Field validation for 'ImagePath' failed on the 'file' tag\nKey: 'Emoji.ImageStaticPath' Error:Field validation for 'ImageStaticPath' failed on the 'file' tag") - - e.ImageStaticPath = "" - err = validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImagePath' Error:Field validation for 'ImagePath' failed on the 'file' tag\nKey: 'Emoji.ImageStaticPath' Error:Field validation for 'ImageStaticPath' failed on the 'required' tag") - - e.ImageStaticPath = "???????????thisnot a valid path####" - err = validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImagePath' Error:Field validation for 'ImagePath' failed on the 'file' tag\nKey: 'Emoji.ImageStaticPath' Error:Field validation for 'ImageStaticPath' failed on the 'file' tag") -} - -func (suite *EmojiValidateTestSuite) TestValidateEmojiURI() { - e := happyEmoji() - - e.URI = "aaaaaaaaaa" - err := validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.URI' Error:Field validation for 'URI' failed on the 'url' tag") - - e.URI = "" - err = validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.URI' Error:Field validation for 'URI' failed on the 'url' tag") -} - -func (suite *EmojiValidateTestSuite) TestValidateEmojiURLCombos() { - e := happyEmoji() - - e.ImageRemoteURL = "" - err := validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImageRemoteURL' Error:Field validation for 'ImageRemoteURL' failed on the 'required_without' tag\nKey: 'Emoji.ImageURL' Error:Field validation for 'ImageURL' failed on the 'required_without' tag") - - e.ImageURL = "https://whatever.org" - err = validate.Struct(e) - suite.NoError(err) - - e.ImageStaticRemoteURL = "" - err = validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImageStaticRemoteURL' Error:Field validation for 'ImageStaticRemoteURL' failed on the 'required_without' tag\nKey: 'Emoji.ImageStaticURL' Error:Field validation for 'ImageStaticURL' failed on the 'required_without' tag") - - e.ImageStaticURL = "https://whatever.org" - err = validate.Struct(e) - suite.NoError(err) - - e.ImageURL = "" - e.ImageStaticURL = "" - e.ImageRemoteURL = "" - e.ImageStaticRemoteURL = "" - err = validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImageRemoteURL' Error:Field validation for 'ImageRemoteURL' failed on the 'required_without' tag\nKey: 'Emoji.ImageStaticRemoteURL' Error:Field validation for 'ImageStaticRemoteURL' failed on the 'required_without' tag\nKey: 'Emoji.ImageURL' Error:Field validation for 'ImageURL' failed on the 'required_without' tag\nKey: 'Emoji.ImageStaticURL' Error:Field validation for 'ImageStaticURL' failed on the 'required_without' tag") -} - -func (suite *EmojiValidateTestSuite) TestValidateFileSize() { - e := happyEmoji() - - e.ImageFileSize = 0 - err := validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImageFileSize' Error:Field validation for 'ImageFileSize' failed on the 'required' tag") - - e.ImageStaticFileSize = 0 - err = validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImageFileSize' Error:Field validation for 'ImageFileSize' failed on the 'required' tag\nKey: 'Emoji.ImageStaticFileSize' Error:Field validation for 'ImageStaticFileSize' failed on the 'required' tag") - - e.ImageFileSize = -1 - err = validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImageFileSize' Error:Field validation for 'ImageFileSize' failed on the 'min' tag\nKey: 'Emoji.ImageStaticFileSize' Error:Field validation for 'ImageStaticFileSize' failed on the 'required' tag") - - e.ImageStaticFileSize = -1 - err = validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImageFileSize' Error:Field validation for 'ImageFileSize' failed on the 'min' tag\nKey: 'Emoji.ImageStaticFileSize' Error:Field validation for 'ImageStaticFileSize' failed on the 'min' tag") -} - -func (suite *EmojiValidateTestSuite) TestValidateDomain() { - e := happyEmoji() - - e.Domain = "" - err := validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.ImageURL' Error:Field validation for 'ImageURL' failed on the 'required_without' tag\nKey: 'Emoji.ImageStaticURL' Error:Field validation for 'ImageStaticURL' failed on the 'required_without' tag") - - e.Domain = "aaaaaaaaa" - err = validate.Struct(e) - suite.EqualError(err, "Key: 'Emoji.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag") -} - -func TestEmojiValidateTestSuite(t *testing.T) { - suite.Run(t, new(EmojiValidateTestSuite)) -} diff --git a/internal/validate/follow_test.go b/internal/validate/follow_test.go deleted file mode 100644 index 2128be4aa..000000000 --- a/internal/validate/follow_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyFollow() *gtsmodel.Follow { - return >smodel.Follow{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - AccountID: "01FE96MAE58MXCE5C4SSMEMCEK", - Account: nil, - TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A", - TargetAccount: nil, - URI: "https://example.org/users/user1/activity/follow/01FE91RJR88PSEEE30EV35QR8N", - } -} - -type FollowValidateTestSuite struct { - suite.Suite -} - -func (suite *FollowValidateTestSuite) TestValidateFollowHappyPath() { - // no problem here - f := happyFollow() - err := validate.Struct(f) - suite.NoError(err) -} - -func (suite *FollowValidateTestSuite) TestValidateFollowBadID() { - f := happyFollow() - - f.ID = "" - err := validate.Struct(f) - suite.EqualError(err, "Key: 'Follow.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - f.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(f) - suite.EqualError(err, "Key: 'Follow.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *FollowValidateTestSuite) TestValidateFollowNoCreatedAt() { - f := happyFollow() - - f.CreatedAt = time.Time{} - err := validate.Struct(f) - suite.NoError(err) -} - -func (suite *FollowValidateTestSuite) TestValidateFollowNoURI() { - f := happyFollow() - - f.URI = "" - err := validate.Struct(f) - suite.EqualError(err, "Key: 'Follow.URI' Error:Field validation for 'URI' failed on the 'required' tag") - - f.URI = "this-is-not-a-valid-url" - err = validate.Struct(f) - suite.EqualError(err, "Key: 'Follow.URI' Error:Field validation for 'URI' failed on the 'url' tag") -} - -func TestFollowValidateTestSuite(t *testing.T) { - suite.Run(t, new(FollowValidateTestSuite)) -} diff --git a/internal/validate/followrequest_test.go b/internal/validate/followrequest_test.go deleted file mode 100644 index 0c3d883bb..000000000 --- a/internal/validate/followrequest_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyFollowRequest() *gtsmodel.FollowRequest { - return >smodel.FollowRequest{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - AccountID: "01FE96MAE58MXCE5C4SSMEMCEK", - Account: nil, - TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A", - TargetAccount: nil, - URI: "https://example.org/users/user1/activity/follow/01FE91RJR88PSEEE30EV35QR8N", - } -} - -type FollowRequestValidateTestSuite struct { - suite.Suite -} - -func (suite *FollowRequestValidateTestSuite) TestValidateFollowRequestHappyPath() { - // no problem here - f := happyFollowRequest() - err := validate.Struct(f) - suite.NoError(err) -} - -func (suite *FollowRequestValidateTestSuite) TestValidateFollowRequestBadID() { - f := happyFollowRequest() - - f.ID = "" - err := validate.Struct(f) - suite.EqualError(err, "Key: 'FollowRequest.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - f.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(f) - suite.EqualError(err, "Key: 'FollowRequest.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *FollowRequestValidateTestSuite) TestValidateFollowRequestNoCreatedAt() { - f := happyFollowRequest() - - f.CreatedAt = time.Time{} - err := validate.Struct(f) - suite.NoError(err) -} - -func (suite *FollowRequestValidateTestSuite) TestValidateFollowRequestNoURI() { - f := happyFollowRequest() - - f.URI = "" - err := validate.Struct(f) - suite.EqualError(err, "Key: 'FollowRequest.URI' Error:Field validation for 'URI' failed on the 'required' tag") - - f.URI = "this-is-not-a-valid-url" - err = validate.Struct(f) - suite.EqualError(err, "Key: 'FollowRequest.URI' Error:Field validation for 'URI' failed on the 'url' tag") -} - -func TestFollowRequestValidateTestSuite(t *testing.T) { - suite.Run(t, new(FollowRequestValidateTestSuite)) -} diff --git a/internal/validate/instance_test.go b/internal/validate/instance_test.go deleted file mode 100644 index 38c68a616..000000000 --- a/internal/validate/instance_test.go +++ /dev/null @@ -1,145 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyInstance() *gtsmodel.Instance { - return >smodel.Instance{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - Domain: "example.org", - Title: "Example Instance", - URI: "https://example.org", - SuspendedAt: time.Time{}, - DomainBlockID: "", - DomainBlock: nil, - ShortDescription: "This is a description for the example/testing instance.", - Description: "This is a way longer description for the example/testing instance!", - Terms: "Don't be a knobhead.", - ContactEmail: "admin@example.org", - ContactAccountUsername: "admin", - ContactAccountID: "01FEE20H5QWHJDEXAEE9G96PR0", - ContactAccount: nil, - Reputation: 420, - Version: "gotosocial 0.1.0", - } -} - -type InstanceValidateTestSuite struct { - suite.Suite -} - -func (suite *InstanceValidateTestSuite) TestValidateInstanceHappyPath() { - // no problem here - m := happyInstance() - err := validate.Struct(*m) - suite.NoError(err) -} - -func (suite *InstanceValidateTestSuite) TestValidateInstanceBadID() { - m := happyInstance() - - m.ID = "" - err := validate.Struct(*m) - suite.EqualError(err, "Key: 'Instance.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - m.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(*m) - suite.EqualError(err, "Key: 'Instance.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *InstanceValidateTestSuite) TestValidateInstanceAccountURI() { - i := happyInstance() - - i.URI = "" - err := validate.Struct(i) - suite.EqualError(err, "Key: 'Instance.URI' Error:Field validation for 'URI' failed on the 'required' tag") - - i.URI = "---------------------------" - err = validate.Struct(i) - suite.EqualError(err, "Key: 'Instance.URI' Error:Field validation for 'URI' failed on the 'url' tag") -} - -func (suite *InstanceValidateTestSuite) TestValidateInstanceDodgyAccountID() { - i := happyInstance() - - i.ContactAccountID = "9HZJ76B6VXSKF" - err := validate.Struct(i) - suite.EqualError(err, "Key: 'Instance.ContactAccountID' Error:Field validation for 'ContactAccountID' failed on the 'ulid' tag") - - i.ContactAccountID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!" - err = validate.Struct(i) - suite.EqualError(err, "Key: 'Instance.ContactAccountID' Error:Field validation for 'ContactAccountID' failed on the 'ulid' tag") - - i.ContactAccountID = "" - err = validate.Struct(i) - suite.EqualError(err, "Key: 'Instance.ContactAccountID' Error:Field validation for 'ContactAccountID' failed on the 'required_with' tag") - - i.ContactAccountUsername = "" - err = validate.Struct(i) - suite.NoError(err) -} - -func (suite *InstanceValidateTestSuite) TestValidateInstanceDomain() { - i := happyInstance() - - i.Domain = "poopoo" - err := validate.Struct(i) - suite.EqualError(err, "Key: 'Instance.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag") - - i.Domain = "" - err = validate.Struct(i) - suite.EqualError(err, "Key: 'Instance.Domain' Error:Field validation for 'Domain' failed on the 'required' tag") - - i.Domain = "https://aaaaaaaaaaaaah.org" - err = validate.Struct(i) - suite.EqualError(err, "Key: 'Instance.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag") -} - -func (suite *InstanceValidateTestSuite) TestValidateInstanceContactEmail() { - i := happyInstance() - - i.ContactEmail = "poopoo" - err := validate.Struct(i) - suite.EqualError(err, "Key: 'Instance.ContactEmail' Error:Field validation for 'ContactEmail' failed on the 'email' tag") - - i.ContactEmail = "" - err = validate.Struct(i) - suite.NoError(err) -} - -func (suite *InstanceValidateTestSuite) TestValidateInstanceNoCreatedAt() { - i := happyInstance() - - i.CreatedAt = time.Time{} - err := validate.Struct(i) - suite.NoError(err) -} - -func TestInstanceValidateTestSuite(t *testing.T) { - suite.Run(t, new(InstanceValidateTestSuite)) -} diff --git a/internal/validate/mediaattachment_test.go b/internal/validate/mediaattachment_test.go deleted file mode 100644 index 1021319f2..000000000 --- a/internal/validate/mediaattachment_test.go +++ /dev/null @@ -1,230 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "os" - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" - "github.com/superseriousbusiness/gotosocial/testrig" -) - -func happyMediaAttachment() *gtsmodel.MediaAttachment { - // the file validator actually runs os.Stat on given paths, so we need to just create small - // temp files for both the main attachment file and the thumbnail - - mainFile, err := os.CreateTemp("", "gts_test_mainfile") - if err != nil { - panic(err) - } - if _, err := mainFile.WriteString("main"); err != nil { - panic(err) - } - mainPath := mainFile.Name() - if err := mainFile.Close(); err != nil { - panic(err) - } - - thumbnailFile, err := os.CreateTemp("", "gts_test_thumbnail") - if err != nil { - panic(err) - } - if _, err := thumbnailFile.WriteString("thumbnail"); err != nil { - panic(err) - } - thumbnailPath := thumbnailFile.Name() - if err := thumbnailFile.Close(); err != nil { - panic(err) - } - - return >smodel.MediaAttachment{ - ID: "01F8MH6NEM8D7527KZAECTCR76", - CreatedAt: time.Now().Add(-71 * time.Hour), - UpdatedAt: time.Now().Add(-71 * time.Hour), - StatusID: "01F8MH75CBF9JFX4ZAD54N0W0R", - URL: "http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpg", - RemoteURL: "", - Type: gtsmodel.FileTypeImage, - FileMeta: gtsmodel.FileMeta{ - Original: gtsmodel.Original{ - Width: 1200, - Height: 630, - Size: 756000, - Aspect: 1.9047619047619047, - }, - Small: gtsmodel.Small{ - Width: 256, - Height: 134, - Size: 34304, - Aspect: 1.9104477611940298, - }, - }, - AccountID: "01F8MH17FWEB39HZJ76B6VXSKF", - Description: "Black and white image of some 50's style text saying: Welcome On Board", - ScheduledStatusID: "", - Blurhash: "LNJRdVM{00Rj%Mayt7j[4nWBofRj", - Processing: 2, - File: gtsmodel.File{ - Path: mainPath, - ContentType: "image/jpeg", - FileSize: 62529, - UpdatedAt: time.Now().Add(-71 * time.Hour), - }, - Thumbnail: gtsmodel.Thumbnail{ - Path: thumbnailPath, - ContentType: "image/jpeg", - FileSize: 6872, - UpdatedAt: time.Now().Add(-71 * time.Hour), - URL: "http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/small/01F8MH6NEM8D7527KZAECTCR76.jpg", - RemoteURL: "", - }, - Avatar: testrig.FalseBool(), - Header: testrig.FalseBool(), - } -} - -type MediaAttachmentValidateTestSuite struct { - suite.Suite -} - -func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentHappyPath() { - // no problem here - m := happyMediaAttachment() - err := validate.Struct(m) - suite.NoError(err) -} - -func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBadFilePaths() { - m := happyMediaAttachment() - - m.File.Path = "/tmp/nonexistent/file/for/gotosocial/test" - err := validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.File.Path' Error:Field validation for 'Path' failed on the 'file' tag") - - m.File.Path = "" - err = validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.File.Path' Error:Field validation for 'Path' failed on the 'required' tag") - - m.File.Path = "???????????thisnot a valid path####" - err = validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.File.Path' Error:Field validation for 'Path' failed on the 'file' tag") - - m.Thumbnail.Path = "/tmp/nonexistent/file/for/gotosocial/test" - err = validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.File.Path' Error:Field validation for 'Path' failed on the 'file' tag\nKey: 'MediaAttachment.Thumbnail.Path' Error:Field validation for 'Path' failed on the 'file' tag") - - m.Thumbnail.Path = "" - err = validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.File.Path' Error:Field validation for 'Path' failed on the 'file' tag\nKey: 'MediaAttachment.Thumbnail.Path' Error:Field validation for 'Path' failed on the 'required' tag") - - m.Thumbnail.Path = "???????????thisnot a valid path####" - err = validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.File.Path' Error:Field validation for 'Path' failed on the 'file' tag\nKey: 'MediaAttachment.Thumbnail.Path' Error:Field validation for 'Path' failed on the 'file' tag") -} - -func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBadType() { - m := happyMediaAttachment() - - m.Type = "" - err := validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.Type' Error:Field validation for 'Type' failed on the 'oneof' tag") - - m.Type = "Not Supported" - err = validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.Type' Error:Field validation for 'Type' failed on the 'oneof' tag") -} - -func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBadFileMeta() { - m := happyMediaAttachment() - - m.FileMeta.Original.Aspect = 0 - err := validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.FileMeta.Original.Aspect' Error:Field validation for 'Aspect' failed on the 'required_with' tag") - - m.FileMeta.Original.Height = 0 - err = validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.FileMeta.Original.Height' Error:Field validation for 'Height' failed on the 'required_with' tag\nKey: 'MediaAttachment.FileMeta.Original.Aspect' Error:Field validation for 'Aspect' failed on the 'required_with' tag") - - m.FileMeta.Original = gtsmodel.Original{} - err = validate.Struct(m) - suite.NoError(err) - - m.FileMeta.Focus.X = 3.6 - err = validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.FileMeta.Focus.X' Error:Field validation for 'X' failed on the 'max' tag") - - m.FileMeta.Focus.Y = -50 - err = validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.FileMeta.Focus.X' Error:Field validation for 'X' failed on the 'max' tag\nKey: 'MediaAttachment.FileMeta.Focus.Y' Error:Field validation for 'Y' failed on the 'min' tag") -} - -func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBadURLCombos() { - m := happyMediaAttachment() - - m.URL = "aaaaaaaaaa" - err := validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.URL' Error:Field validation for 'URL' failed on the 'url' tag") - - m.URL = "" - err = validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.URL' Error:Field validation for 'URL' failed on the 'required_without' tag\nKey: 'MediaAttachment.RemoteURL' Error:Field validation for 'RemoteURL' failed on the 'required_without' tag") - - m.RemoteURL = "oooooooooo" - err = validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.RemoteURL' Error:Field validation for 'RemoteURL' failed on the 'url' tag") - - m.RemoteURL = "https://a-valid-url.gay" - err = validate.Struct(m) - suite.NoError(err) -} - -func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBlurhash() { - m := happyMediaAttachment() - - m.Blurhash = "" - err := validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.Blurhash' Error:Field validation for 'Blurhash' failed on the 'required_if' tag") - - m.Type = gtsmodel.FileTypeAudio - err = validate.Struct(m) - suite.NoError(err) - - m.Blurhash = "some_blurhash" - err = validate.Struct(m) - suite.NoError(err) -} - -func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentProcessing() { - m := happyMediaAttachment() - - m.Processing = 420 - err := validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.Processing' Error:Field validation for 'Processing' failed on the 'oneof' tag") - - m.Processing = -5 - err = validate.Struct(m) - suite.EqualError(err, "Key: 'MediaAttachment.Processing' Error:Field validation for 'Processing' failed on the 'oneof' tag") -} - -func TestMediaAttachmentValidateTestSuite(t *testing.T) { - suite.Run(t, new(MediaAttachmentValidateTestSuite)) -} diff --git a/internal/validate/mention_test.go b/internal/validate/mention_test.go deleted file mode 100644 index 52513bd8c..000000000 --- a/internal/validate/mention_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyMention() *gtsmodel.Mention { - return >smodel.Mention{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - OriginAccountID: "01FE96MAE58MXCE5C4SSMEMCEK", - OriginAccountURI: "https://some-instance/accounts/bleepbloop", - OriginAccount: nil, - TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A", - TargetAccount: nil, - StatusID: "01FE96NBPNJNY26730FT6GZTFE", - Status: nil, - } -} - -type MentionValidateTestSuite struct { - suite.Suite -} - -func (suite *MentionValidateTestSuite) TestValidateMentionHappyPath() { - // no problem here - m := happyMention() - err := validate.Struct(m) - suite.NoError(err) -} - -func (suite *MentionValidateTestSuite) TestValidateMentionBadID() { - m := happyMention() - - m.ID = "" - err := validate.Struct(m) - suite.EqualError(err, "Key: 'Mention.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - m.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(m) - suite.EqualError(err, "Key: 'Mention.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *MentionValidateTestSuite) TestValidateMentionAccountURI() { - m := happyMention() - - m.OriginAccountURI = "" - err := validate.Struct(m) - suite.EqualError(err, "Key: 'Mention.OriginAccountURI' Error:Field validation for 'OriginAccountURI' failed on the 'url' tag") - - m.OriginAccountURI = "---------------------------" - err = validate.Struct(m) - suite.EqualError(err, "Key: 'Mention.OriginAccountURI' Error:Field validation for 'OriginAccountURI' failed on the 'url' tag") -} - -func (suite *MentionValidateTestSuite) TestValidateMentionDodgyStatusID() { - m := happyMention() - - m.StatusID = "9HZJ76B6VXSKF" - err := validate.Struct(m) - suite.EqualError(err, "Key: 'Mention.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") - - m.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!" - err = validate.Struct(m) - suite.EqualError(err, "Key: 'Mention.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") -} - -func (suite *MentionValidateTestSuite) TestValidateMentionNoCreatedAt() { - m := happyMention() - - m.CreatedAt = time.Time{} - err := validate.Struct(m) - suite.NoError(err) -} - -func TestMentionValidateTestSuite(t *testing.T) { - suite.Run(t, new(MentionValidateTestSuite)) -} diff --git a/internal/validate/notification_test.go b/internal/validate/notification_test.go deleted file mode 100644 index a76b8bf58..000000000 --- a/internal/validate/notification_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyNotification() *gtsmodel.Notification { - return >smodel.Notification{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - NotificationType: gtsmodel.NotificationFave, - OriginAccountID: "01FE96MAE58MXCE5C4SSMEMCEK", - OriginAccount: nil, - TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A", - TargetAccount: nil, - StatusID: "01FE96NBPNJNY26730FT6GZTFE", - Status: nil, - } -} - -type NotificationValidateTestSuite struct { - suite.Suite -} - -func (suite *NotificationValidateTestSuite) TestValidateNotificationHappyPath() { - // no problem here - n := happyNotification() - err := validate.Struct(n) - suite.NoError(err) -} - -func (suite *NotificationValidateTestSuite) TestValidateNotificationBadID() { - n := happyNotification() - - n.ID = "" - err := validate.Struct(n) - suite.EqualError(err, "Key: 'Notification.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - n.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(n) - suite.EqualError(err, "Key: 'Notification.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *NotificationValidateTestSuite) TestValidateNotificationStatusID() { - n := happyNotification() - - n.StatusID = "" - err := validate.Struct(n) - suite.EqualError(err, "Key: 'Notification.StatusID' Error:Field validation for 'StatusID' failed on the 'required_if' tag") - - n.StatusID = "9HZJ76B6VXSKF" - err = validate.Struct(n) - suite.EqualError(err, "Key: 'Notification.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") - - n.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!" - err = validate.Struct(n) - suite.EqualError(err, "Key: 'Notification.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") - - n.StatusID = "" - n.NotificationType = gtsmodel.NotificationFollowRequest - err = validate.Struct(n) - suite.NoError(err) -} - -func (suite *NotificationValidateTestSuite) TestValidateNotificationNoCreatedAt() { - n := happyNotification() - - n.CreatedAt = time.Time{} - err := validate.Struct(n) - suite.NoError(err) -} - -func TestNotificationValidateTestSuite(t *testing.T) { - suite.Run(t, new(NotificationValidateTestSuite)) -} diff --git a/internal/validate/routersession_test.go b/internal/validate/routersession_test.go deleted file mode 100644 index f9dc49e40..000000000 --- a/internal/validate/routersession_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyRouterSession() *gtsmodel.RouterSession { - return >smodel.RouterSession{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - Auth: []byte("12345678901234567890123456789012"), - Crypt: []byte("12345678901234567890123456789012"), - } -} - -type RouterSessionValidateTestSuite struct { - suite.Suite -} - -func (suite *RouterSessionValidateTestSuite) TestValidateRouterSessionHappyPath() { - // no problem here - r := happyRouterSession() - err := validate.Struct(r) - suite.NoError(err) -} - -func (suite *RouterSessionValidateTestSuite) TestValidateRouterSessionAuth() { - r := happyRouterSession() - - // remove auth struct - r.Auth = nil - err := validate.Struct(r) - suite.EqualError(err, "Key: 'RouterSession.Auth' Error:Field validation for 'Auth' failed on the 'required' tag") - - // auth bytes too long - r.Auth = []byte("1234567890123456789012345678901234567890") - err = validate.Struct(r) - suite.EqualError(err, "Key: 'RouterSession.Auth' Error:Field validation for 'Auth' failed on the 'len' tag") - - // auth bytes too short - r.Auth = []byte("12345678901") - err = validate.Struct(r) - suite.EqualError(err, "Key: 'RouterSession.Auth' Error:Field validation for 'Auth' failed on the 'len' tag") -} - -func (suite *RouterSessionValidateTestSuite) TestValidateRouterSessionCrypt() { - r := happyRouterSession() - - // remove crypt struct - r.Crypt = nil - err := validate.Struct(r) - suite.EqualError(err, "Key: 'RouterSession.Crypt' Error:Field validation for 'Crypt' failed on the 'required' tag") - - // crypt bytes too long - r.Crypt = []byte("1234567890123456789012345678901234567890") - err = validate.Struct(r) - suite.EqualError(err, "Key: 'RouterSession.Crypt' Error:Field validation for 'Crypt' failed on the 'len' tag") - - // crypt bytes too short - r.Crypt = []byte("12345678901") - err = validate.Struct(r) - suite.EqualError(err, "Key: 'RouterSession.Crypt' Error:Field validation for 'Crypt' failed on the 'len' tag") -} - -func TestRouterSessionValidateTestSuite(t *testing.T) { - suite.Run(t, new(RouterSessionValidateTestSuite)) -} diff --git a/internal/validate/status_test.go b/internal/validate/status_test.go deleted file mode 100644 index db0cd8fda..000000000 --- a/internal/validate/status_test.go +++ /dev/null @@ -1,160 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/ap" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" - "github.com/superseriousbusiness/gotosocial/testrig" -) - -func happyStatus() *gtsmodel.Status { - return >smodel.Status{ - ID: "01FEBBH6NYDG87NK6A6EC543ED", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - URI: "https://example.org/users/test_user/statuses/01FEBBH6NYDG87NK6A6EC543ED", - URL: "https://example.org/@test_user/01FEBBH6NYDG87NK6A6EC543ED", - Content: "

Test status! #hello

", - AttachmentIDs: []string{"01FEBBKZBY9H5FEP3PHVVAAGN1", "01FEBBM7S2R4WT6WWW22KN1PWE"}, - Attachments: nil, - TagIDs: []string{"01FEBBNBMBSN1FESMZ1TCXNWYP"}, - Tags: nil, - MentionIDs: nil, - Mentions: nil, - EmojiIDs: nil, - Emojis: nil, - Local: testrig.TrueBool(), - AccountID: "01FEBBQ4KEP3824WW61MF52638", - Account: nil, - AccountURI: "https://example.org/users/test_user", - InReplyToID: "", - InReplyToURI: "", - InReplyToAccountID: "", - InReplyTo: nil, - InReplyToAccount: nil, - BoostOfID: "", - BoostOfAccountID: "", - BoostOf: nil, - BoostOfAccount: nil, - ContentWarning: "hello world test post", - Visibility: gtsmodel.VisibilityPublic, - Sensitive: testrig.FalseBool(), - Language: "en", - CreatedWithApplicationID: "01FEBBZHF4GFVRXSJVXD0JTZZ2", - CreatedWithApplication: nil, - Federated: testrig.TrueBool(), - Boostable: testrig.TrueBool(), - Replyable: testrig.TrueBool(), - Likeable: testrig.TrueBool(), - ActivityStreamsType: ap.ObjectNote, - Text: "Test status! #hello", - } -} - -type StatusValidateTestSuite struct { - suite.Suite -} - -func (suite *StatusValidateTestSuite) TestValidateStatusHappyPath() { - // no problem here - s := happyStatus() - err := validate.Struct(s) - suite.NoError(err) -} - -func (suite *StatusValidateTestSuite) TestValidateStatusBadID() { - s := happyStatus() - - s.ID = "" - err := validate.Struct(s) - suite.EqualError(err, "Key: 'Status.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - s.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(s) - suite.EqualError(err, "Key: 'Status.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *StatusValidateTestSuite) TestValidateStatusAttachmentIDs() { - s := happyStatus() - - s.AttachmentIDs[0] = "" - err := validate.Struct(s) - suite.EqualError(err, "Key: 'Status.AttachmentIDs[0]' Error:Field validation for 'AttachmentIDs[0]' failed on the 'ulid' tag") - - s.AttachmentIDs[0] = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(s) - suite.EqualError(err, "Key: 'Status.AttachmentIDs[0]' Error:Field validation for 'AttachmentIDs[0]' failed on the 'ulid' tag") - - s.AttachmentIDs[1] = "" - err = validate.Struct(s) - suite.EqualError(err, "Key: 'Status.AttachmentIDs[0]' Error:Field validation for 'AttachmentIDs[0]' failed on the 'ulid' tag\nKey: 'Status.AttachmentIDs[1]' Error:Field validation for 'AttachmentIDs[1]' failed on the 'ulid' tag") - - s.AttachmentIDs = []string{} - err = validate.Struct(s) - suite.NoError(err) - - s.AttachmentIDs = nil - err = validate.Struct(s) - suite.NoError(err) -} - -func (suite *StatusValidateTestSuite) TestStatusApplicationID() { - s := happyStatus() - - s.CreatedWithApplicationID = "" - err := validate.Struct(s) - suite.EqualError(err, "Key: 'Status.CreatedWithApplicationID' Error:Field validation for 'CreatedWithApplicationID' failed on the 'required_if' tag") - - s.Local = testrig.FalseBool() - err = validate.Struct(s) - suite.NoError(err) -} - -func (suite *StatusValidateTestSuite) TestValidateStatusReplyFields() { - s := happyStatus() - - s.InReplyToAccountID = "01FEBCTP6DN7961PN81C3DVM4N " - err := validate.Struct(s) - suite.EqualError(err, "Key: 'Status.InReplyToID' Error:Field validation for 'InReplyToID' failed on the 'required_with' tag\nKey: 'Status.InReplyToURI' Error:Field validation for 'InReplyToURI' failed on the 'required_with' tag\nKey: 'Status.InReplyToAccountID' Error:Field validation for 'InReplyToAccountID' failed on the 'ulid' tag") - - s.InReplyToAccountID = "01FEBCTP6DN7961PN81C3DVM4N" - err = validate.Struct(s) - suite.EqualError(err, "Key: 'Status.InReplyToID' Error:Field validation for 'InReplyToID' failed on the 'required_with' tag\nKey: 'Status.InReplyToURI' Error:Field validation for 'InReplyToURI' failed on the 'required_with' tag") - - s.InReplyToURI = "https://example.org/users/mmbop/statuses/aaaaaaaa" - err = validate.Struct(s) - suite.EqualError(err, "Key: 'Status.InReplyToID' Error:Field validation for 'InReplyToID' failed on the 'required_with' tag") - - s.InReplyToID = "not a valid ulid" - err = validate.Struct(s) - suite.EqualError(err, "Key: 'Status.InReplyToID' Error:Field validation for 'InReplyToID' failed on the 'ulid' tag") - - s.InReplyToID = "01FEBD07E72DEY6YB9K10ZA6ST" - err = validate.Struct(s) - suite.NoError(err) -} - -func TestStatusValidateTestSuite(t *testing.T) { - suite.Run(t, new(StatusValidateTestSuite)) -} diff --git a/internal/validate/statusbookmark_test.go b/internal/validate/statusbookmark_test.go deleted file mode 100644 index 3be9e56ed..000000000 --- a/internal/validate/statusbookmark_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyStatusBookmark() *gtsmodel.StatusBookmark { - return >smodel.StatusBookmark{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - AccountID: "01FE96MAE58MXCE5C4SSMEMCEK", - Account: nil, - TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A", - TargetAccount: nil, - StatusID: "01FE96NBPNJNY26730FT6GZTFE", - Status: nil, - } -} - -type StatusBookmarkValidateTestSuite struct { - suite.Suite -} - -func (suite *StatusBookmarkValidateTestSuite) TestValidateStatusBookmarkHappyPath() { - // no problem here - s := happyStatusBookmark() - err := validate.Struct(s) - suite.NoError(err) -} - -func (suite *StatusBookmarkValidateTestSuite) TestValidateStatusBookmarkBadID() { - s := happyStatusBookmark() - - s.ID = "" - err := validate.Struct(s) - suite.EqualError(err, "Key: 'StatusBookmark.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - s.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(s) - suite.EqualError(err, "Key: 'StatusBookmark.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *StatusBookmarkValidateTestSuite) TestValidateStatusBookmarkDodgyStatusID() { - s := happyStatusBookmark() - - s.StatusID = "9HZJ76B6VXSKF" - err := validate.Struct(s) - suite.EqualError(err, "Key: 'StatusBookmark.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") - - s.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!" - err = validate.Struct(s) - suite.EqualError(err, "Key: 'StatusBookmark.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") -} - -func (suite *StatusBookmarkValidateTestSuite) TestValidateStatusBookmarkNoCreatedAt() { - s := happyStatusBookmark() - - s.CreatedAt = time.Time{} - err := validate.Struct(s) - suite.NoError(err) -} - -func TestStatusBookmarkValidateTestSuite(t *testing.T) { - suite.Run(t, new(StatusBookmarkValidateTestSuite)) -} diff --git a/internal/validate/statusfave_test.go b/internal/validate/statusfave_test.go deleted file mode 100644 index e864e39f2..000000000 --- a/internal/validate/statusfave_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyStatusFave() *gtsmodel.StatusFave { - return >smodel.StatusFave{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - AccountID: "01FE96MAE58MXCE5C4SSMEMCEK", - Account: nil, - TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A", - TargetAccount: nil, - StatusID: "01FE96NBPNJNY26730FT6GZTFE", - Status: nil, - URI: "https://example.org/users/user1/activity/faves/01FE91RJR88PSEEE30EV35QR8N", - } -} - -type StatusFaveValidateTestSuite struct { - suite.Suite -} - -func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveHappyPath() { - // no problem here - f := happyStatusFave() - err := validate.Struct(f) - suite.NoError(err) -} - -func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveBadID() { - f := happyStatusFave() - - f.ID = "" - err := validate.Struct(f) - suite.EqualError(err, "Key: 'StatusFave.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - f.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(f) - suite.EqualError(err, "Key: 'StatusFave.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveDodgyStatusID() { - f := happyStatusFave() - - f.StatusID = "9HZJ76B6VXSKF" - err := validate.Struct(f) - suite.EqualError(err, "Key: 'StatusFave.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") - - f.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!" - err = validate.Struct(f) - suite.EqualError(err, "Key: 'StatusFave.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") -} - -func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveNoCreatedAt() { - f := happyStatusFave() - - f.CreatedAt = time.Time{} - err := validate.Struct(f) - suite.NoError(err) -} - -func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveNoURI() { - f := happyStatusFave() - - f.URI = "" - err := validate.Struct(f) - suite.EqualError(err, "Key: 'StatusFave.URI' Error:Field validation for 'URI' failed on the 'required' tag") - - f.URI = "this-is-not-a-valid-url" - err = validate.Struct(f) - suite.EqualError(err, "Key: 'StatusFave.URI' Error:Field validation for 'URI' failed on the 'url' tag") -} - -func TestStatusFaveValidateTestSuite(t *testing.T) { - suite.Run(t, new(StatusFaveValidateTestSuite)) -} diff --git a/internal/validate/statusmute_test.go b/internal/validate/statusmute_test.go deleted file mode 100644 index 20358bb23..000000000 --- a/internal/validate/statusmute_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyStatusMute() *gtsmodel.StatusMute { - return >smodel.StatusMute{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - AccountID: "01FE96MAE58MXCE5C4SSMEMCEK", - Account: nil, - TargetAccountID: "01FE96MXRHWZHKC0WH5FT82H1A", - TargetAccount: nil, - StatusID: "01FE96NBPNJNY26730FT6GZTFE", - Status: nil, - } -} - -type StatusMuteValidateTestSuite struct { - suite.Suite -} - -func (suite *StatusMuteValidateTestSuite) TestValidateStatusMuteHappyPath() { - // no problem here - m := happyStatusMute() - err := validate.Struct(m) - suite.NoError(err) -} - -func (suite *StatusMuteValidateTestSuite) TestValidateStatusMuteBadID() { - m := happyStatusMute() - - m.ID = "" - err := validate.Struct(m) - suite.EqualError(err, "Key: 'StatusMute.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - m.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(m) - suite.EqualError(err, "Key: 'StatusMute.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *StatusMuteValidateTestSuite) TestValidateStatusMuteDodgyStatusID() { - m := happyStatusMute() - - m.StatusID = "9HZJ76B6VXSKF" - err := validate.Struct(m) - suite.EqualError(err, "Key: 'StatusMute.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") - - m.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!" - err = validate.Struct(m) - suite.EqualError(err, "Key: 'StatusMute.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") -} - -func (suite *StatusMuteValidateTestSuite) TestValidateStatusMuteNoCreatedAt() { - m := happyStatusMute() - - m.CreatedAt = time.Time{} - err := validate.Struct(m) - suite.NoError(err) -} - -func TestStatusMuteValidateTestSuite(t *testing.T) { - suite.Run(t, new(StatusMuteValidateTestSuite)) -} diff --git a/internal/validate/structvalidation.go b/internal/validate/structvalidation.go deleted file mode 100644 index 573e79fd2..000000000 --- a/internal/validate/structvalidation.go +++ /dev/null @@ -1,67 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate - -import ( - "reflect" - - "github.com/go-playground/validator/v10" - "github.com/superseriousbusiness/gotosocial/internal/regexes" -) - -var v *validator.Validate - -func ulidValidator(fl validator.FieldLevel) bool { - field := fl.Field() - - switch field.Kind() { - case reflect.String: - return regexes.ULID.MatchString(field.String()) - default: - return false - } -} - -func init() { - v = validator.New() - if err := v.RegisterValidation("ulid", ulidValidator); err != nil { - panic(err) - } -} - -// Struct validates the passed struct, returning validator.ValidationErrors if invalid, or nil if OK. -func Struct(s interface{}) error { - return processValidationError(v.Struct(s)) -} - -func processValidationError(err error) error { - if err == nil { - return nil - } - - if ive, ok := err.(*validator.InvalidValidationError); ok { - panic(ive) - } - - valErr, ok := err.(validator.ValidationErrors) - if !ok { - panic("*validator.InvalidValidationError could not be coerced to validator.ValidationErrors") - } - - return valErr -} diff --git a/internal/validate/structvalidation_test.go b/internal/validate/structvalidation_test.go deleted file mode 100644 index f8ad514cf..000000000 --- a/internal/validate/structvalidation_test.go +++ /dev/null @@ -1,70 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -type ValidateTestSuite struct { - suite.Suite -} - -func (suite *ValidateTestSuite) TestValidateNilPointer() { - var nilUser *gtsmodel.User - suite.Panics(func() { - validate.Struct(nilUser) - }) -} - -func (suite *ValidateTestSuite) TestValidatePointer() { - user := >smodel.User{} - err := validate.Struct(user) - suite.EqualError(err, "Key: 'User.ID' Error:Field validation for 'ID' failed on the 'required' tag\nKey: 'User.AccountID' Error:Field validation for 'AccountID' failed on the 'required' tag\nKey: 'User.EncryptedPassword' Error:Field validation for 'EncryptedPassword' failed on the 'required' tag\nKey: 'User.UnconfirmedEmail' Error:Field validation for 'UnconfirmedEmail' failed on the 'required_without' tag") -} - -func (suite *ValidateTestSuite) TestValidateNil() { - suite.Panics(func() { - validate.Struct(nil) - }) -} - -func (suite *ValidateTestSuite) TestValidateWeirdULID() { - type a struct { - ID bool `validate:"required,ulid"` - } - - err := validate.Struct(a{ID: true}) - suite.Error(err) -} - -func (suite *ValidateTestSuite) TestValidateNotStruct() { - type aaaaaaa string - aaaaaa := aaaaaaa("aaaa") - suite.Panics(func() { - validate.Struct(aaaaaa) - }) -} - -func TestValidateTestSuite(t *testing.T) { - suite.Run(t, new(ValidateTestSuite)) -} diff --git a/internal/validate/token_test.go b/internal/validate/token_test.go deleted file mode 100644 index 2ff0e0721..000000000 --- a/internal/validate/token_test.go +++ /dev/null @@ -1,98 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" -) - -func happyToken() *gtsmodel.Token { - return >smodel.Token{ - ID: "01FE91RJR88PSEEE30EV35QR8N", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - ClientID: "01FEEDMF6C0QD589MRK7919Z0R", - UserID: "01FEK0BFJKYXB4Y51RBQ7P5P79", - RedirectURI: "oauth2redirect://com.keylesspalace.tusky/", - Scope: "read write follow", - } -} - -type TokenValidateTestSuite struct { - suite.Suite -} - -func (suite *TokenValidateTestSuite) TestValidateTokenHappyPath() { - // no problem here - t := happyToken() - err := validate.Struct(t) - suite.NoError(err) -} - -func (suite *TokenValidateTestSuite) TestValidateTokenBadID() { - t := happyToken() - - t.ID = "" - err := validate.Struct(t) - suite.EqualError(err, "Key: 'Token.ID' Error:Field validation for 'ID' failed on the 'required' tag") - - t.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(t) - suite.EqualError(err, "Key: 'Token.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") -} - -func (suite *TokenValidateTestSuite) TestValidateTokenNoCreatedAt() { - t := happyToken() - - t.CreatedAt = time.Time{} - err := validate.Struct(t) - suite.NoError(err) -} - -func (suite *TokenValidateTestSuite) TestValidateTokenRedirectURI() { - t := happyToken() - - t.RedirectURI = "invalid-uri" - err := validate.Struct(t) - suite.EqualError(err, "Key: 'Token.RedirectURI' Error:Field validation for 'RedirectURI' failed on the 'uri' tag") - - t.RedirectURI = "" - err = validate.Struct(t) - suite.EqualError(err, "Key: 'Token.RedirectURI' Error:Field validation for 'RedirectURI' failed on the 'required' tag") - - t.RedirectURI = "urn:ietf:wg:oauth:2.0:oob" - err = validate.Struct(t) - suite.NoError(err) -} - -func (suite *TokenValidateTestSuite) TestValidateTokenScope() { - t := happyToken() - - t.Scope = "" - err := validate.Struct(t) - suite.EqualError(err, "Key: 'Token.Scope' Error:Field validation for 'Scope' failed on the 'required' tag") -} - -func TestTokenValidateTestSuite(t *testing.T) { - suite.Run(t, new(TokenValidateTestSuite)) -} diff --git a/internal/validate/user_test.go b/internal/validate/user_test.go deleted file mode 100644 index f61ff6e2f..000000000 --- a/internal/validate/user_test.go +++ /dev/null @@ -1,134 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package validate_test - -import ( - "net" - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/validate" - "github.com/superseriousbusiness/gotosocial/testrig" -) - -func happyUser() *gtsmodel.User { - return >smodel.User{ - ID: "01FE8TTK9F34BR0KG7639AJQTX", - Email: "whatever@example.org", - AccountID: "01FE8TWA7CN8J7237K5DFS1RY5", - Account: nil, - EncryptedPassword: "$2y$10$tkRapNGW.RWkEuCMWdgArunABFvsPGRvFQY3OibfSJo0RDL3z8WfC", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - SignUpIP: net.ParseIP("128.64.32.16"), - CurrentSignInAt: time.Now(), - CurrentSignInIP: net.ParseIP("128.64.32.16"), - LastSignInAt: time.Now(), - LastSignInIP: net.ParseIP("128.64.32.16"), - SignInCount: 0, - InviteID: "", - ChosenLanguages: []string{}, - FilteredLanguages: []string{}, - Locale: "en", - CreatedByApplicationID: "01FE8Y5EHMWCA1MHMTNHRVZ1X4", - CreatedByApplication: nil, - LastEmailedAt: time.Now(), - ConfirmationToken: "", - ConfirmedAt: time.Now(), - ConfirmationSentAt: time.Time{}, - UnconfirmedEmail: "", - Moderator: testrig.FalseBool(), - Admin: testrig.FalseBool(), - Disabled: testrig.FalseBool(), - Approved: testrig.TrueBool(), - } -} - -type UserValidateTestSuite struct { - suite.Suite -} - -func (suite *UserValidateTestSuite) TestValidateUserHappyPath() { - // no problem here - u := happyUser() - err := validate.Struct(u) - suite.NoError(err) -} - -func (suite *UserValidateTestSuite) TestValidateUserNoID() { - // user has no id set - u := happyUser() - u.ID = "" - - err := validate.Struct(u) - suite.EqualError(err, "Key: 'User.ID' Error:Field validation for 'ID' failed on the 'required' tag") -} - -func (suite *UserValidateTestSuite) TestValidateUserNoEmail() { - // user has no email or unconfirmed email set - u := happyUser() - u.Email = "" - - err := validate.Struct(u) - suite.EqualError(err, "Key: 'User.Email' Error:Field validation for 'Email' failed on the 'required_with' tag\nKey: 'User.UnconfirmedEmail' Error:Field validation for 'UnconfirmedEmail' failed on the 'required_without' tag") -} - -func (suite *UserValidateTestSuite) TestValidateUserOnlyUnconfirmedEmail() { - // user has only UnconfirmedEmail but ConfirmedAt is set - u := happyUser() - u.Email = "" - u.UnconfirmedEmail = "whatever@example.org" - - err := validate.Struct(u) - suite.EqualError(err, "Key: 'User.Email' Error:Field validation for 'Email' failed on the 'required_with' tag") -} - -func (suite *UserValidateTestSuite) TestValidateUserOnlyUnconfirmedEmailOK() { - // user has only UnconfirmedEmail and ConfirmedAt is not set - u := happyUser() - u.Email = "" - u.UnconfirmedEmail = "whatever@example.org" - u.ConfirmedAt = time.Time{} - - err := validate.Struct(u) - suite.NoError(err) -} - -func (suite *UserValidateTestSuite) TestValidateUserNoConfirmedAt() { - // user has Email but no ConfirmedAt - u := happyUser() - u.ConfirmedAt = time.Time{} - - err := validate.Struct(u) - suite.EqualError(err, "Key: 'User.ConfirmedAt' Error:Field validation for 'ConfirmedAt' failed on the 'required_with' tag") -} - -func (suite *UserValidateTestSuite) TestValidateUserUnlikelySignInCount() { - // user has Email but no ConfirmedAt - u := happyUser() - u.SignInCount = -69 - - err := validate.Struct(u) - suite.EqualError(err, "Key: 'User.SignInCount' Error:Field validation for 'SignInCount' failed on the 'min' tag") -} - -func TestUserValidateTestSuite(t *testing.T) { - suite.Run(t, new(UserValidateTestSuite)) -}