From 4696e1a7b389599fa981f334b343daa911b11f5d Mon Sep 17 00:00:00 2001 From: tsmethurst Date: Wed, 1 Sep 2021 18:29:25 +0200 Subject: moving stuff around --- internal/validate/block_test.go | 116 ++++++++++++ internal/validate/domainblock_test.go | 122 +++++++++++++ internal/validate/emaildomainblock_test.go | 97 ++++++++++ internal/validate/emoji_test.go | 195 ++++++++++++++++++++ internal/validate/follow_test.go | 88 +++++++++ internal/validate/followrequest_test.go | 88 +++++++++ internal/validate/formvalidation.go | 182 +++++++++++++++++++ internal/validate/formvalidation_test.go | 283 +++++++++++++++++++++++++++++ internal/validate/instance_test.go | 146 +++++++++++++++ internal/validate/mediaattachment_test.go | 230 +++++++++++++++++++++++ internal/validate/mention_test.go | 102 +++++++++++ internal/validate/notification_test.go | 98 ++++++++++ internal/validate/routersession_test.go | 88 +++++++++ internal/validate/status_test.go | 163 +++++++++++++++++ internal/validate/statusbookmark_test.go | 88 +++++++++ internal/validate/statusfave_test.go | 101 ++++++++++ internal/validate/statusmute_test.go | 88 +++++++++ internal/validate/structvalidation.go | 75 ++++++++ internal/validate/structvalidation_test.go | 65 +++++++ internal/validate/tag_test.go | 93 ++++++++++ internal/validate/user_test.go | 125 +++++++++++++ 21 files changed, 2633 insertions(+) create mode 100644 internal/validate/block_test.go create mode 100644 internal/validate/domainblock_test.go create mode 100644 internal/validate/emaildomainblock_test.go create mode 100644 internal/validate/emoji_test.go create mode 100644 internal/validate/follow_test.go create mode 100644 internal/validate/followrequest_test.go create mode 100644 internal/validate/formvalidation.go create mode 100644 internal/validate/formvalidation_test.go create mode 100644 internal/validate/instance_test.go create mode 100644 internal/validate/mediaattachment_test.go create mode 100644 internal/validate/mention_test.go create mode 100644 internal/validate/notification_test.go create mode 100644 internal/validate/routersession_test.go create mode 100644 internal/validate/status_test.go create mode 100644 internal/validate/statusbookmark_test.go create mode 100644 internal/validate/statusfave_test.go create mode 100644 internal/validate/statusmute_test.go create mode 100644 internal/validate/structvalidation.go create mode 100644 internal/validate/structvalidation_test.go create mode 100644 internal/validate/tag_test.go create mode 100644 internal/validate/user_test.go (limited to 'internal/validate') diff --git a/internal/validate/block_test.go b/internal/validate/block_test.go new file mode 100644 index 000000000..0b3293fb5 --- /dev/null +++ b/internal/validate/block_test.go @@ -0,0 +1,116 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 + d := happyBlock() + err := validate.Struct(*d) + suite.NoError(err) +} + +func (suite *BlockValidateTestSuite) TestValidateBlockBadID() { + d := happyBlock() + + d.ID = "" + err := validate.Struct(*d) + suite.EqualError(err, "Key: 'Block.ID' Error:Field validation for 'ID' failed on the 'required' tag") + + d.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" + err = validate.Struct(*d) + suite.EqualError(err, "Key: 'Block.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") +} + +func (suite *BlockValidateTestSuite) TestValidateBlockNoCreatedAt() { + d := happyBlock() + + d.CreatedAt = time.Time{} + err := validate.Struct(*d) + suite.NoError(err) +} + +func (suite *BlockValidateTestSuite) TestValidateBlockCreatedByAccountID() { + d := happyBlock() + + d.AccountID = "" + err := validate.Struct(*d) + suite.EqualError(err, "Key: 'Block.AccountID' Error:Field validation for 'AccountID' failed on the 'required' tag") + + d.AccountID = "this-is-not-a-valid-ulid" + err = validate.Struct(*d) + suite.EqualError(err, "Key: 'Block.AccountID' Error:Field validation for 'AccountID' failed on the 'ulid' tag") +} + +func (suite *BlockValidateTestSuite) TestValidateBlockTargetAccountID() { + d := happyBlock() + + d.TargetAccountID = "invalid-ulid" + err := validate.Struct(*d) + suite.EqualError(err, "Key: 'Block.TargetAccountID' Error:Field validation for 'TargetAccountID' failed on the 'ulid' tag") + + d.TargetAccountID = "01FEEDHX4G7EGHF5GD9E82Y51Q" + err = validate.Struct(*d) + suite.NoError(err) + + d.TargetAccountID = "" + err = validate.Struct(*d) + suite.EqualError(err, "Key: 'Block.TargetAccountID' Error:Field validation for 'TargetAccountID' failed on the 'required' tag") +} + +func (suite *BlockValidateTestSuite) TestValidateBlockURI() { + d := happyBlock() + + d.URI = "invalid-uri" + err := validate.Struct(*d) + suite.EqualError(err, "Key: 'Block.URI' Error:Field validation for 'URI' failed on the 'url' tag") + + d.URI = "" + err = validate.Struct(*d) + 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/domainblock_test.go b/internal/validate/domainblock_test.go new file mode 100644 index 000000000..0ce826f12 --- /dev/null +++ b/internal/validate/domainblock_test.go @@ -0,0 +1,122 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 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: false, + 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 new file mode 100644 index 000000000..26ba08615 --- /dev/null +++ b/internal/validate/emaildomainblock_test.go @@ -0,0 +1,97 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 new file mode 100644 index 000000000..4ada1ca8b --- /dev/null +++ b/internal/validate/emoji_test.go @@ -0,0 +1,195 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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" +) + +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: false, + URI: "https://example.org/emojis/blob_test", + VisibleInPicker: true, + 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 new file mode 100644 index 000000000..d624666be --- /dev/null +++ b/internal/validate/follow_test.go @@ -0,0 +1,88 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 new file mode 100644 index 000000000..f607e24c3 --- /dev/null +++ b/internal/validate/followrequest_test.go @@ -0,0 +1,88 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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/formvalidation.go b/internal/validate/formvalidation.go new file mode 100644 index 000000000..a30ec1a58 --- /dev/null +++ b/internal/validate/formvalidation.go @@ -0,0 +1,182 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 ( + "errors" + "fmt" + "net/mail" + + "github.com/superseriousbusiness/gotosocial/internal/regexes" + pwv "github.com/wagslane/go-password-validator" + "golang.org/x/text/language" +) + +const ( + maximumPasswordLength = 64 + minimumPasswordEntropy = 60 // dictates password strength. See https://github.com/wagslane/go-password-validator + minimumReasonLength = 40 + maximumReasonLength = 500 + maximumSiteTitleLength = 40 + maximumShortDescriptionLength = 500 + maximumDescriptionLength = 5000 + maximumSiteTermsLength = 5000 + maximumUsernameLength = 64 + maximumEmojiShortcodeLength = 30 + maximumHashtagLength = 30 +) + +// NewPassword returns an error if the given password is not sufficiently strong, or nil if it's ok. +func NewPassword(password string) error { + if password == "" { + return errors.New("no password provided") + } + + if len(password) > maximumPasswordLength { + return fmt.Errorf("password should be no more than %d chars", maximumPasswordLength) + } + + return pwv.Validate(password, minimumPasswordEntropy) +} + +// Username makes sure that a given username is valid (ie., letters, numbers, underscores, check length). +// Returns an error if not. +func Username(username string) error { + if username == "" { + return errors.New("no username provided") + } + + if !regexes.Username.MatchString(username) { + return fmt.Errorf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max %d characters", username, maximumUsernameLength) + } + + return nil +} + +// Email makes sure that a given email address is a valid address. +// Returns an error if not. +func Email(email string) error { + if email == "" { + return errors.New("no email provided") + } + + _, err := mail.ParseAddress(email) + return err +} + +// Language checks that the given language string is a 2- or 3-letter ISO 639 code. +// Returns an error if the language cannot be parsed. See: https://pkg.go.dev/golang.org/x/text/language +func Language(lang string) error { + if lang == "" { + return errors.New("no language provided") + } + _, err := language.ParseBase(lang) + return err +} + +// SignUpReason checks that a sufficient reason is given for a server signup request +func SignUpReason(reason string, reasonRequired bool) error { + if !reasonRequired { + // we don't care! + // we're not going to do anything with this text anyway if no reason is required + return nil + } + + if reason == "" { + return errors.New("no reason provided") + } + + if len(reason) < minimumReasonLength { + return fmt.Errorf("reason should be at least %d chars but '%s' was %d", minimumReasonLength, reason, len(reason)) + } + + if len(reason) > maximumReasonLength { + return fmt.Errorf("reason should be no more than %d chars but given reason was %d", maximumReasonLength, len(reason)) + } + return nil +} + +// DisplayName checks that a requested display name is valid +func DisplayName(displayName string) error { + // TODO: add some validation logic here -- length, characters, etc + return nil +} + +// Note checks that a given profile/account note/bio is valid +func Note(note string) error { + // TODO: add some validation logic here -- length, characters, etc + return nil +} + +// Privacy checks that the desired privacy setting is valid +func Privacy(privacy string) error { + // TODO: add some validation logic here -- length, characters, etc + return nil +} + +// EmojiShortcode just runs the given shortcode through the regular expression +// for emoji shortcodes, to figure out whether it's a valid shortcode, ie., 2-30 characters, +// lowercase a-z, numbers, and underscores. +func EmojiShortcode(shortcode string) error { + if !regexes.EmojiShortcode.MatchString(shortcode) { + return fmt.Errorf("shortcode %s did not pass validation, must be between 2 and 30 characters, lowercase letters, numbers, and underscores only", shortcode) + } + return nil +} + +// SiteTitle ensures that the given site title is within spec. +func SiteTitle(siteTitle string) error { + if len(siteTitle) > maximumSiteTitleLength { + return fmt.Errorf("site title should be no more than %d chars but given title was %d", maximumSiteTitleLength, len(siteTitle)) + } + + return nil +} + +// SiteShortDescription ensures that the given site short description is within spec. +func SiteShortDescription(d string) error { + if len(d) > maximumShortDescriptionLength { + return fmt.Errorf("short description should be no more than %d chars but given description was %d", maximumShortDescriptionLength, len(d)) + } + + return nil +} + +// SiteDescription ensures that the given site description is within spec. +func SiteDescription(d string) error { + if len(d) > maximumDescriptionLength { + return fmt.Errorf("description should be no more than %d chars but given description was %d", maximumDescriptionLength, len(d)) + } + + return nil +} + +// SiteTerms ensures that the given site terms string is within spec. +func SiteTerms(t string) error { + if len(t) > maximumSiteTermsLength { + return fmt.Errorf("terms should be no more than %d chars but given terms was %d", maximumSiteTermsLength, len(t)) + } + + return nil +} + +// ULID returns true if the passed string is a valid ULID. +func ULID(i string) bool { + return regexes.ULID.MatchString(i) +} diff --git a/internal/validate/formvalidation_test.go b/internal/validate/formvalidation_test.go new file mode 100644 index 000000000..03bed42d8 --- /dev/null +++ b/internal/validate/formvalidation_test.go @@ -0,0 +1,283 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 ( + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/validate" +) + +type ValidationTestSuite struct { + suite.Suite +} + +func (suite *ValidationTestSuite) TestCheckPasswordStrength() { + empty := "" + terriblePassword := "password" + weakPassword := "OKPassword" + shortPassword := "Ok12" + specialPassword := "Ok12%" + longPassword := "thisisafuckinglongpasswordbutnospecialchars" + tooLong := "Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Quisque a enim nibh. Vestibulum bibendum leo ac porttitor auctor." + strongPassword := "3dX5@Zc%mV*W2MBNEy$@" + var err error + + err = validate.NewPassword(empty) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("no password provided"), err) + } + + err = validate.NewPassword(terriblePassword) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("insecure password, try including more special characters, using uppercase letters, using numbers or using a longer password"), err) + } + + err = validate.NewPassword(weakPassword) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("insecure password, try including more special characters, using numbers or using a longer password"), err) + } + + err = validate.NewPassword(shortPassword) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("insecure password, try including more special characters or using a longer password"), err) + } + + err = validate.NewPassword(specialPassword) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("insecure password, try including more special characters or using a longer password"), err) + } + + err = validate.NewPassword(longPassword) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } + + err = validate.NewPassword(tooLong) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("password should be no more than 64 chars"), err) + } + + err = validate.NewPassword(strongPassword) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } +} + +func (suite *ValidationTestSuite) TestValidateUsername() { + empty := "" + tooLong := "holycrapthisisthelongestusernameiveeverseeninmylifethatstoomuchman" + withSpaces := "this username has spaces in it" + weirdChars := "thisusername&&&&&&&istooweird!!" + leadingSpace := " see_that_leading_space" + trailingSpace := "thisusername_ends_with_a_space " + newlines := "this_is\n_almost_ok" + goodUsername := "this_is_a_good_username" + var err error + + err = validate.Username(empty) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("no username provided"), err) + } + + err = validate.Username(tooLong) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), fmt.Errorf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max 64 characters", tooLong), err) + } + + err = validate.Username(withSpaces) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), fmt.Errorf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max 64 characters", withSpaces), err) + } + + err = validate.Username(weirdChars) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), fmt.Errorf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max 64 characters", weirdChars), err) + } + + err = validate.Username(leadingSpace) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), fmt.Errorf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max 64 characters", leadingSpace), err) + } + + err = validate.Username(trailingSpace) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), fmt.Errorf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max 64 characters", trailingSpace), err) + } + + err = validate.Username(newlines) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), fmt.Errorf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max 64 characters", newlines), err) + } + + err = validate.Username(goodUsername) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } +} + +func (suite *ValidationTestSuite) TestValidateEmail() { + empty := "" + notAnEmailAddress := "this-is-no-email-address!" + almostAnEmailAddress := "@thisisalmostan@email.address" + aWebsite := "https://thisisawebsite.com" + emailAddress := "thisis.actually@anemail.address" + var err error + + err = validate.Email(empty) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("no email provided"), err) + } + + err = validate.Email(notAnEmailAddress) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("mail: missing '@' or angle-addr"), err) + } + + err = validate.Email(almostAnEmailAddress) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("mail: no angle-addr"), err) + } + + err = validate.Email(aWebsite) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("mail: missing '@' or angle-addr"), err) + } + + err = validate.Email(emailAddress) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } +} + +func (suite *ValidationTestSuite) TestValidateLanguage() { + empty := "" + notALanguage := "this isn't a language at all!" + english := "en" + capitalEnglish := "EN" + arabic3Letters := "ara" + mixedCapsEnglish := "eN" + englishUS := "en-us" + dutch := "nl" + german := "de" + var err error + + err = validate.Language(empty) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("no language provided"), err) + } + + err = validate.Language(notALanguage) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("language: tag is not well-formed"), err) + } + + err = validate.Language(english) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } + + err = validate.Language(capitalEnglish) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } + + err = validate.Language(arabic3Letters) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } + + err = validate.Language(mixedCapsEnglish) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } + + err = validate.Language(englishUS) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("language: tag is not well-formed"), err) + } + + err = validate.Language(dutch) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } + + err = validate.Language(german) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } +} + +func (suite *ValidationTestSuite) TestValidateReason() { + empty := "" + badReason := "because" + goodReason := "to smash the state and destroy capitalism ultimately and completely" + tooLong := "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris auctor mollis viverra. Maecenas maximus mollis sem, nec fermentum velit consectetur non. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Quisque a enim nibh. Vestibulum bibendum leo ac porttitor auctor. Curabitur velit tellus, facilisis vitae lorem a, ullamcorper efficitur leo. Sed a auctor tortor. Sed ut finibus ante, sit amet laoreet sapien. Donec ullamcorper tellus a nibh sodales vulputate. Donec id dolor eu odio mollis bibendum. Pellentesque habitant morbi tristique senectus et netus at." + var err error + + // check with no reason required + err = validate.SignUpReason(empty, false) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } + + err = validate.SignUpReason(badReason, false) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } + + err = validate.SignUpReason(tooLong, false) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } + + err = validate.SignUpReason(goodReason, false) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } + + // check with reason required + err = validate.SignUpReason(empty, true) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("no reason provided"), err) + } + + err = validate.SignUpReason(badReason, true) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("reason should be at least 40 chars but 'because' was 7"), err) + } + + err = validate.SignUpReason(tooLong, true) + if assert.Error(suite.T(), err) { + assert.Equal(suite.T(), errors.New("reason should be no more than 500 chars but given reason was 600"), err) + } + + err = validate.SignUpReason(goodReason, true) + if assert.NoError(suite.T(), err) { + assert.Equal(suite.T(), nil, err) + } +} + +func TestValidationTestSuite(t *testing.T) { + suite.Run(t, new(ValidationTestSuite)) +} diff --git a/internal/validate/instance_test.go b/internal/validate/instance_test.go new file mode 100644 index 000000000..6042da80e --- /dev/null +++ b/internal/validate/instance_test.go @@ -0,0 +1,146 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 new file mode 100644 index 000000000..84c2fa4eb --- /dev/null +++ b/internal/validate/mediaattachment_test.go @@ -0,0 +1,230 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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" +) + +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.jpeg", + 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.jpeg", + RemoteURL: "", + }, + Avatar: false, + Header: false, + } +} + +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 new file mode 100644 index 000000000..59ba1e3fd --- /dev/null +++ b/internal/validate/mention_test.go @@ -0,0 +1,102 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 new file mode 100644 index 000000000..700560f2a --- /dev/null +++ b/internal/validate/notification_test.go @@ -0,0 +1,98 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 + m := happyNotification() + err := validate.Struct(*m) + suite.NoError(err) +} + +func (suite *NotificationValidateTestSuite) TestValidateNotificationBadID() { + m := happyNotification() + + m.ID = "" + err := validate.Struct(*m) + suite.EqualError(err, "Key: 'Notification.ID' Error:Field validation for 'ID' failed on the 'required' tag") + + m.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" + err = validate.Struct(*m) + suite.EqualError(err, "Key: 'Notification.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") +} + +func (suite *NotificationValidateTestSuite) TestValidateNotificationStatusID() { + m := happyNotification() + + m.StatusID = "" + err := validate.Struct(*m) + suite.EqualError(err, "Key: 'Notification.StatusID' Error:Field validation for 'StatusID' failed on the 'required_if' tag") + + m.StatusID = "9HZJ76B6VXSKF" + err = validate.Struct(*m) + suite.EqualError(err, "Key: 'Notification.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") + + m.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!" + err = validate.Struct(*m) + suite.EqualError(err, "Key: 'Notification.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") + + m.StatusID = "" + m.NotificationType = gtsmodel.NotificationFollowRequest + err = validate.Struct(*m) + suite.NoError(err) +} + +func (suite *NotificationValidateTestSuite) TestValidateNotificationNoCreatedAt() { + m := happyNotification() + + m.CreatedAt = time.Time{} + err := validate.Struct(*m) + 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 new file mode 100644 index 000000000..7ab4034d1 --- /dev/null +++ b/internal/validate/routersession_test.go @@ -0,0 +1,88 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 new file mode 100644 index 000000000..57e7259b9 --- /dev/null +++ b/internal/validate/status_test.go @@ -0,0 +1,163 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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" +) + +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: true, + 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: false, + Language: "en", + CreatedWithApplicationID: "01FEBBZHF4GFVRXSJVXD0JTZZ2", + CreatedWithApplication: nil, + VisibilityAdvanced: gtsmodel.VisibilityAdvanced{ + Federated: true, + Boostable: true, + Replyable: true, + Likeable: true, + }, + ActivityStreamsType: ap.ObjectNote, + Text: "Test status! #hello", + Pinned: false, + } +} + +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 = false + 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 new file mode 100644 index 000000000..667994aa0 --- /dev/null +++ b/internal/validate/statusbookmark_test.go @@ -0,0 +1,88 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 + m := happyStatusBookmark() + err := validate.Struct(*m) + suite.NoError(err) +} + +func (suite *StatusBookmarkValidateTestSuite) TestValidateStatusBookmarkBadID() { + m := happyStatusBookmark() + + m.ID = "" + err := validate.Struct(*m) + suite.EqualError(err, "Key: 'StatusBookmark.ID' Error:Field validation for 'ID' failed on the 'required' tag") + + m.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" + err = validate.Struct(*m) + suite.EqualError(err, "Key: 'StatusBookmark.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") +} + +func (suite *StatusBookmarkValidateTestSuite) TestValidateStatusBookmarkDodgyStatusID() { + m := happyStatusBookmark() + + m.StatusID = "9HZJ76B6VXSKF" + err := validate.Struct(*m) + suite.EqualError(err, "Key: 'StatusBookmark.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") + + m.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!" + err = validate.Struct(*m) + suite.EqualError(err, "Key: 'StatusBookmark.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") +} + +func (suite *StatusBookmarkValidateTestSuite) TestValidateStatusBookmarkNoCreatedAt() { + m := happyStatusBookmark() + + m.CreatedAt = time.Time{} + err := validate.Struct(*m) + 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 new file mode 100644 index 000000000..52bef1749 --- /dev/null +++ b/internal/validate/statusfave_test.go @@ -0,0 +1,101 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 new file mode 100644 index 000000000..de23114eb --- /dev/null +++ b/internal/validate/statusmute_test.go @@ -0,0 +1,88 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 new file mode 100644 index 000000000..7717822d9 --- /dev/null +++ b/internal/validate/structvalidation.go @@ -0,0 +1,75 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 + +// Validation Panic messages +const ( + PointerPanic = "validate function was passed pointer" + InvalidPanic = "validate function was passed invalid item" +) + +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() + v.RegisterValidation("ulid", ulidValidator) +} + +// Struct validates the passed struct, returning validator.ValidationErrors if invalid, or nil if OK. +func Struct(s interface{}) error { + switch reflect.ValueOf(s).Kind() { + case reflect.Invalid: + panic(InvalidPanic) + case reflect.Ptr: + panic(PointerPanic) + } + + err := v.Struct(s) + return processValidationError(err) +} + +func processValidationError(err error) error { + if err == nil { + return nil + } + + if ive, ok := err.(*validator.InvalidValidationError); ok { + panic(ive) + } + + return err.(validator.ValidationErrors) +} diff --git a/internal/validate/structvalidation_test.go b/internal/validate/structvalidation_test.go new file mode 100644 index 000000000..606d92da4 --- /dev/null +++ b/internal/validate/structvalidation_test.go @@ -0,0 +1,65 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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) TestValidatePointer() { + var nilUser *gtsmodel.User + suite.PanicsWithValue(validate.PointerPanic, func() { + validate.Struct(nilUser) + }) +} + +func (suite *ValidateTestSuite) TestValidateNil() { + suite.PanicsWithValue(validate.InvalidPanic, 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/tag_test.go b/internal/validate/tag_test.go new file mode 100644 index 000000000..4f8ab2fba --- /dev/null +++ b/internal/validate/tag_test.go @@ -0,0 +1,93 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 happyTag() *gtsmodel.Tag { + return >smodel.Tag{ + ID: "01FE91RJR88PSEEE30EV35QR8N", + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + URL: "https://example.org/tags/some_tag", + Name: "some_tag", + FirstSeenFromAccountID: "01FE91SR5P2GW06K3AJ98P72MT", + Useable: true, + Listable: true, + LastStatusAt: time.Now(), + } +} + +type TagValidateTestSuite struct { + suite.Suite +} + +func (suite *TagValidateTestSuite) TestValidateTagHappyPath() { + // no problem here + t := happyTag() + err := validate.Struct(*t) + suite.NoError(err) +} + +func (suite *TagValidateTestSuite) TestValidateTagNoName() { + t := happyTag() + t.Name = "" + + err := validate.Struct(*t) + suite.EqualError(err, "Key: 'Tag.Name' Error:Field validation for 'Name' failed on the 'required' tag") +} + +func (suite *TagValidateTestSuite) TestValidateTagBadURL() { + t := happyTag() + + t.URL = "" + err := validate.Struct(*t) + suite.EqualError(err, "Key: 'Tag.URL' Error:Field validation for 'URL' failed on the 'required' tag") + + t.URL = "no-schema.com" + err = validate.Struct(*t) + suite.EqualError(err, "Key: 'Tag.URL' Error:Field validation for 'URL' failed on the 'url' tag") + + t.URL = "justastring" + err = validate.Struct(*t) + suite.EqualError(err, "Key: 'Tag.URL' Error:Field validation for 'URL' failed on the 'url' tag") + + t.URL = "https://aaa\n\n\naaaaaaaa" + err = validate.Struct(*t) + suite.EqualError(err, "Key: 'Tag.URL' Error:Field validation for 'URL' failed on the 'url' tag") +} + +func (suite *TagValidateTestSuite) TestValidateTagNoFirstSeenFromAccountID() { + t := happyTag() + t.FirstSeenFromAccountID = "" + + err := validate.Struct(*t) + suite.NoError(err) +} + +func TestTagValidateTestSuite(t *testing.T) { + suite.Run(t, new(TagValidateTestSuite)) +} diff --git a/internal/validate/user_test.go b/internal/validate/user_test.go new file mode 100644 index 000000000..5a13a510f --- /dev/null +++ b/internal/validate/user_test.go @@ -0,0 +1,125 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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" +) + +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: false, + Admin: false, + Disabled: false, + Approved: true, + } +} + +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 TestUserValidateTestSuite(t *testing.T) { + suite.Run(t, new(UserValidateTestSuite)) +} -- cgit v1.3 From 6f7da0ecdd8b99a4385b74abc61532a615e9ef26 Mon Sep 17 00:00:00 2001 From: tsmethurst Date: Thu, 2 Sep 2021 12:10:59 +0200 Subject: account + application tests --- internal/gtsmodel/account.go | 82 ++++---- internal/gtsmodel/application.go | 4 +- internal/validate/account_test.go | 343 ++++++++++++++++++++++++++++++++++ internal/validate/application_test.go | 133 +++++++++++++ lint.sh | 2 +- 5 files changed, 520 insertions(+), 44 deletions(-) create mode 100644 internal/validate/account_test.go create mode 100644 internal/validate/application_test.go (limited to 'internal/validate') diff --git a/internal/gtsmodel/account.go b/internal/gtsmodel/account.go index 4f385b3a6..67ce05d0b 100644 --- a/internal/gtsmodel/account.go +++ b/internal/gtsmodel/account.go @@ -29,47 +29,47 @@ 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:timestamp,nullzero,notnull,default:current_timestamp"` // when was item created - UpdatedAt time.Time `validate:"-" bun:"type:timestamp,nullzero,notnull,default:current_timestamp"` // when was item last updated - Username string `validate:"required" bun:",nullzero,notnull,unique:userdomain"` // 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:userdomain"` // Domain of the account, will be null if this is a local account, otherwise something like ``example.org`` or ``mastodon.social``. 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:",nullzero"` // DisplayName for this account. Can be empty, then just the Username will be used for display purposes. - Fields []Field `validate:"-"` // a key/value map of fields that this account has added to their profile - Note string `validate:"-" bun:",nullzero"` // A note that this account has on their profile (ie., the account's bio/description of themselves) - Memorial bool `validate:"-" bun:",nullzero,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 - MovedToAccountID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // This account has moved this account id in the database - Bot bool `validate:"-" bun:",nullzero,default:false"` // Does this account identify itself as a bot? - Reason string `validate:"-" bun:",nullzero"` // What reason was given for signing up when this account was created? - Locked bool `validate:"-" bun:",nullzero,default:true"` // Does this account need an approval for new followers? - Discoverable bool `validate:"-" bun:",nullzero,default:false"` // Should this account be shown in the instance's profile directory? - Privacy Visibility `validate:"oneof=public unlocked followers_only mutuals_only direct" bun:",nullzero,notnull,default:'public'"` // Default post privacy for this account - Sensitive bool `validate:"-" bun:",nullzero,default:false"` // Set posts from this account to sensitive by default? - Language string `validate:"-" bun:",nullzero,notnull,default:'en'"` // What language does this account post in? - URI string `validate:"required,url" bun:",nullzero,notnull,unique"` // ActivityPub URI for this account. - URL string `validate:"omitempty,url" bun:",nullzero,unique"` // Web URL for this account's profile - LastWebfingeredAt time.Time `validate:"required_with=Domain" bun:"type:timestamp,nullzero"` // Last time this account was refreshed/located with webfinger. - InboxURI string `validate:"omitempty,url" bun:",nullzero,unique"` // Address of this account's ActivityPub inbox, for sending activity to - OutboxURI string `validate:"omitempty,url" bun:",nullzero,unique"` // Address of this account's activitypub outbox - FollowingURI string `validate:"omitempty,url" bun:",nullzero,unique"` // URI for getting the following list of this account - FollowersURI string `validate:"omitempty,url" bun:",nullzero,unique"` // URI for getting the followers list of this account - FeaturedCollectionURI string `validate:"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"` // Privatekey for validating activitypub requests, will only be defined for local accounts - PublicKey *rsa.PublicKey `validate:"required"` // 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:timestamp,nullzero"` // When was this account set to have all its media shown as sensitive? - SilencedAt time.Time `validate:"-" bun:"type:timestamp,nullzero"` // When was this account silenced (eg., statuses only visible to followers, not public)? - SuspendedAt time.Time `validate:"-" bun:"type:timestamp,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:",nullzero,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 + 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:timestamp,nullzero,notnull,default:current_timestamp"` // when was item created + UpdatedAt time.Time `validate:"-" bun:"type:timestamp,nullzero,notnull,default:current_timestamp"` // when was item last updated + Username string `validate:"required" bun:",nullzero,notnull,unique:userdomain"` // 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:userdomain"` // Domain of the account, will be null if this is a local account, otherwise something like ``example.org`` or ``mastodon.social``. 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:",nullzero"` // DisplayName for this account. Can be empty, then just the Username will be used for display purposes. + Fields []Field `validate:"-"` // a key/value map of fields that this account has added to their profile + Note string `validate:"-" bun:",nullzero"` // A note that this account has on their profile (ie., the account's bio/description of themselves) + Memorial bool `validate:"-" bun:",nullzero,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 + MovedToAccountID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // This account has moved this account id in the database + Bot bool `validate:"-" bun:",nullzero,default:false"` // Does this account identify itself as a bot? + Reason string `validate:"-" bun:",nullzero"` // What reason was given for signing up when this account was created? + Locked bool `validate:"-" bun:",nullzero,default:true"` // Does this account need an approval for new followers? + Discoverable bool `validate:"-" bun:",nullzero,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:",nullzero,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? + 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 + LastWebfingeredAt time.Time `validate:"required_with=Domain" bun:"type:timestamp,nullzero"` // Last time this account was refreshed/located with webfinger. + InboxURI string `validate:"required_without=Domain,omitempty,url" bun:",nullzero,unique"` // Address of this account's ActivityPub inbox, for sending activity to + 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"` // Privatekey for validating activitypub requests, will only be defined for local accounts + PublicKey *rsa.PublicKey `validate:"required"` // 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:timestamp,nullzero"` // When was this account set to have all its media shown as sensitive? + SilencedAt time.Time `validate:"-" bun:"type:timestamp,nullzero"` // When was this account silenced (eg., statuses only visible to followers, not public)? + SuspendedAt time.Time `validate:"-" bun:"type:timestamp,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:",nullzero,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 } // Field represents a key value field on an account, for things like pronouns, website, etc. diff --git a/internal/gtsmodel/application.go b/internal/gtsmodel/application.go index 30035123e..27ea61000 100644 --- a/internal/gtsmodel/application.go +++ b/internal/gtsmodel/application.go @@ -28,8 +28,8 @@ type Application struct { UpdatedAt time.Time `validate:"-" bun:"type:timestamp,nullzero,notnull,default:current_timestamp"` // when was item last updated Name string `validate:"required" bun:",nullzero,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" bun:",nullzero,notnull"` // redirect uri requested by the application for oauth2 flow + RedirectURI string `validate:"required,uri" bun:",nullzero,notnull"` // redirect uri requested by the application for oauth2 flow ClientID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // 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:",nullzero,notnull,default:'read'"` // scopes requested when this app was created + Scopes string `validate:"-" bun:",nullzero,notnull,default:'read'"` // scopes requested when this app was created } diff --git a/internal/validate/account_test.go b/internal/validate/account_test.go new file mode 100644 index 000000000..8fcdcfb38 --- /dev/null +++ b/internal/validate/account_test.go @@ -0,0 +1,343 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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" +) + +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: false, + AlsoKnownAs: "", + MovedToAccountID: "", + Bot: false, + Reason: "I wanna be on this damned webbed site so bad! Please! Wow", + Locked: false, + Discoverable: true, + Privacy: gtsmodel.VisibilityPublic, + Sensitive: false, + Language: "en", + URI: "http://localhost:8080/users/the_mighty_zork", + URL: "http://localhost:8080/@the_mighty_zork", + LastWebfingeredAt: 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: false, + 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) +} + +// LastWebfingeredAt must be defined if remote account +func (suite *AccountValidateTestSuite) TestValidateAccountNoWebfingeredAt() { + a := happyAccount() + + a.Domain = "example.org" + a.LastWebfingeredAt = time.Time{} + err := validate.Struct(*a) + suite.EqualError(err, "Key: 'Account.LastWebfingeredAt' Error:Field validation for 'LastWebfingeredAt' 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.LastWebfingeredAt = 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.LastWebfingeredAt = 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.LastWebfingeredAt = 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.LastWebfingeredAt = 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 new file mode 100644 index 000000000..38fe0d8ff --- /dev/null +++ b/internal/validate/application_test.go @@ -0,0 +1,133 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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.NoError(err) +} + +func TestApplicationValidateTestSuite(t *testing.T) { + suite.Run(t, new(ApplicationValidateTestSuite)) +} diff --git a/lint.sh b/lint.sh index db661c243..da7fe05b5 100755 --- a/lint.sh +++ b/lint.sh @@ -1,3 +1,3 @@ #!/bin/bash -golangci-lint run \ No newline at end of file +golangci-lint run --test=false -- cgit v1.3 From 42892f91e1695a0aaf1608b8146303d9d52947d9 Mon Sep 17 00:00:00 2001 From: tsmethurst Date: Thu, 2 Sep 2021 12:17:14 +0200 Subject: client_test --- internal/gtsmodel/client.go | 2 +- internal/validate/client_test.go | 102 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 internal/validate/client_test.go (limited to 'internal/validate') diff --git a/internal/gtsmodel/client.go b/internal/gtsmodel/client.go index de9bd569a..e924bd190 100644 --- a/internal/gtsmodel/client.go +++ b/internal/gtsmodel/client.go @@ -26,6 +26,6 @@ type Client struct { CreatedAt time.Time `validate:"-" bun:"type:timestamp,nullzero,notnull,default:current_timestamp"` // when was item created UpdatedAt time.Time `validate:"-" bun:"type:timestamp,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" bun:",nullzero,notnull"` // domain requested for client + 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 } diff --git a/internal/validate/client_test.go b/internal/validate/client_test.go new file mode 100644 index 000000000..d3c2466cf --- /dev/null +++ b/internal/validate/client_test.go @@ -0,0 +1,102 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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() { + a := happyClient() + + a.Secret = "invalid-uuid" + err := validate.Struct(*a) + suite.EqualError(err, "Key: 'Client.Secret' Error:Field validation for 'Secret' failed on the 'uuid' tag") + + a.Secret = "" + err = validate.Struct(*a) + 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)) +} -- cgit v1.3 From cc7a35ee9299750667a5e53542c285ca05634706 Mon Sep 17 00:00:00 2001 From: tsmethurst Date: Thu, 2 Sep 2021 12:22:09 +0200 Subject: token tests --- internal/gtsmodel/token.go | 4 +- internal/validate/token_test.go | 99 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 internal/validate/token_test.go (limited to 'internal/validate') diff --git a/internal/gtsmodel/token.go b/internal/gtsmodel/token.go index 65728ac60..4188ea9eb 100644 --- a/internal/gtsmodel/token.go +++ b/internal/gtsmodel/token.go @@ -27,8 +27,8 @@ type Token struct { UpdatedAt time.Time `validate:"-" bun:"type:timestamp,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,notnull"` // ID of the user who owns this token - RedirectURI string `validate:"required,url" bun:",nullzero,notnull"` // Oauth redirect URI for this token - Scope string `validate:"omitempty,url" bun:",nullzero,notnull,default:'read'"` // Oauth scope + RedirectURI string `validate:"required,uri" bun:",nullzero,notnull"` // Oauth redirect URI for this token + Scope string `validate:"omitempty" bun:",nullzero,notnull,default:'read'"` // 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 diff --git a/internal/validate/token_test.go b/internal/validate/token_test.go new file mode 100644 index 000000000..bf12328a2 --- /dev/null +++ b/internal/validate/token_test.go @@ -0,0 +1,99 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + 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 + a := happyToken() + err := validate.Struct(*a) + suite.NoError(err) +} + +func (suite *TokenValidateTestSuite) TestValidateTokenBadID() { + a := happyToken() + + a.ID = "" + err := validate.Struct(*a) + suite.EqualError(err, "Key: 'Token.ID' Error:Field validation for 'ID' failed on the 'required' tag") + + a.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" + err = validate.Struct(*a) + suite.EqualError(err, "Key: 'Token.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") +} + +func (suite *TokenValidateTestSuite) TestValidateTokenNoCreatedAt() { + a := happyToken() + + a.CreatedAt = time.Time{} + err := validate.Struct(*a) + suite.NoError(err) +} + +func (suite *TokenValidateTestSuite) TestValidateTokenRedirectURI() { + a := happyToken() + + a.RedirectURI = "invalid-uri" + err := validate.Struct(*a) + suite.EqualError(err, "Key: 'Token.RedirectURI' Error:Field validation for 'RedirectURI' failed on the 'uri' tag") + + a.RedirectURI = "" + err = validate.Struct(*a) + suite.EqualError(err, "Key: 'Token.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 *TokenValidateTestSuite) TestValidateTokenScope() { + a := happyToken() + + a.Scope = "" + err := validate.Struct(*a) + suite.NoError(err) +} + +func TestTokenValidateTestSuite(t *testing.T) { + suite.Run(t, new(TokenValidateTestSuite)) +} -- cgit v1.3 From 5d5327614d1ad03fb6110c80bed1fa945376cbb4 Mon Sep 17 00:00:00 2001 From: tsmethurst Date: Thu, 2 Sep 2021 12:24:18 +0200 Subject: lint --- internal/regexes/regexes.go | 28 ++++++++++++++-------------- internal/validate/formvalidation.go | 4 ++-- internal/validate/structvalidation.go | 4 +++- 3 files changed, 19 insertions(+), 17 deletions(-) (limited to 'internal/validate') diff --git a/internal/regexes/regexes.go b/internal/regexes/regexes.go index 53446ff2c..8ac31ef62 100644 --- a/internal/regexes/regexes.go +++ b/internal/regexes/regexes.go @@ -24,20 +24,20 @@ import ( ) const ( - users = "users" - actors = "actors" - statuses = "statuses" - inbox = "inbox" - outbox = "outbox" - followers = "followers" - following = "following" - liked = "liked" - collections = "collections" - featured = "featured" - publicKey = "main-key" - follow = "follow" - update = "updates" - blocks = "blocks" + users = "users" + actors = "actors" + statuses = "statuses" + inbox = "inbox" + outbox = "outbox" + followers = "followers" + following = "following" + liked = "liked" + // collections = "collections" + // featured = "featured" + publicKey = "main-key" + follow = "follow" + // update = "updates" + blocks = "blocks" ) const ( diff --git a/internal/validate/formvalidation.go b/internal/validate/formvalidation.go index a30ec1a58..9f61578e7 100644 --- a/internal/validate/formvalidation.go +++ b/internal/validate/formvalidation.go @@ -38,8 +38,8 @@ const ( maximumDescriptionLength = 5000 maximumSiteTermsLength = 5000 maximumUsernameLength = 64 - maximumEmojiShortcodeLength = 30 - maximumHashtagLength = 30 + // maximumEmojiShortcodeLength = 30 + // maximumHashtagLength = 30 ) // NewPassword returns an error if the given password is not sufficiently strong, or nil if it's ok. diff --git a/internal/validate/structvalidation.go b/internal/validate/structvalidation.go index 7717822d9..3c7a4abd9 100644 --- a/internal/validate/structvalidation.go +++ b/internal/validate/structvalidation.go @@ -46,7 +46,9 @@ func ulidValidator(fl validator.FieldLevel) bool { func init() { v = validator.New() - v.RegisterValidation("ulid", ulidValidator) + if err := v.RegisterValidation("ulid", ulidValidator); err != nil { + panic(err) + } } // Struct validates the passed struct, returning validator.ValidationErrors if invalid, or nil if OK. -- cgit v1.3 From a1166768149ef9d6c948c73b07190008e3207999 Mon Sep 17 00:00:00 2001 From: tsmethurst Date: Fri, 3 Sep 2021 10:30:40 +0200 Subject: Review changes --- internal/federation/dereferencing/account.go | 6 ++-- internal/federation/federatingdb/accept.go | 2 +- internal/federation/federatingdb/undo.go | 8 ++--- internal/gtsmodel/application.go | 4 +-- internal/gtsmodel/token.go | 2 +- internal/gtsmodel/user.go | 2 +- internal/validate/application_test.go | 28 +++++++-------- internal/validate/block_test.go | 54 ++++++++++++++-------------- internal/validate/client_test.go | 24 ++++++------- internal/validate/domainblock_test.go | 22 ++++++------ internal/validate/emaildomainblock_test.go | 16 ++++----- internal/validate/emoji_test.go | 38 ++++++++++---------- internal/validate/follow_test.go | 12 +++---- internal/validate/followrequest_test.go | 12 +++---- internal/validate/instance_test.go | 24 ++++++------- internal/validate/mediaattachment_test.go | 46 ++++++++++++------------ internal/validate/mention_test.go | 16 ++++----- internal/validate/notification_test.go | 40 ++++++++++----------- internal/validate/routersession_test.go | 14 ++++---- internal/validate/status_test.go | 30 ++++++++-------- internal/validate/statusbookmark_test.go | 30 ++++++++-------- internal/validate/statusfave_test.go | 16 ++++----- internal/validate/statusmute_test.go | 12 +++---- internal/validate/structvalidation.go | 5 +-- internal/validate/structvalidation_test.go | 12 +++++-- internal/validate/tag_test.go | 14 ++++---- internal/validate/token_test.go | 42 +++++++++++----------- internal/validate/user_test.go | 21 +++++++---- 28 files changed, 282 insertions(+), 270 deletions(-) (limited to 'internal/validate') diff --git a/internal/federation/dereferencing/account.go b/internal/federation/dereferencing/account.go index eb6a3a760..b16b53fee 100644 --- a/internal/federation/dereferencing/account.go +++ b/internal/federation/dereferencing/account.go @@ -165,19 +165,19 @@ func (d *deref) dereferenceAccountable(ctx context.Context, username string, rem } switch t.GetTypeName() { - case string(ap.ActorPerson): + case ap.ActorPerson: p, ok := t.(vocab.ActivityStreamsPerson) if !ok { return nil, errors.New("DereferenceAccountable: error resolving type as activitystreams person") } return p, nil - case string(ap.ActorApplication): + case ap.ActorApplication: p, ok := t.(vocab.ActivityStreamsApplication) if !ok { return nil, errors.New("DereferenceAccountable: error resolving type as activitystreams application") } return p, nil - case string(ap.ActorService): + case ap.ActorService: p, ok := t.(vocab.ActivityStreamsService) if !ok { return nil, errors.New("DereferenceAccountable: error resolving type as activitystreams service") diff --git a/internal/federation/federatingdb/accept.go b/internal/federation/federatingdb/accept.go index ceaf4c4ef..477c5e8b9 100644 --- a/internal/federation/federatingdb/accept.go +++ b/internal/federation/federatingdb/accept.go @@ -118,7 +118,7 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA } switch iter.GetType().GetTypeName() { // we have the whole object so we can figure out what we're accepting - case string(ap.ActivityFollow): + case ap.ActivityFollow: // ACCEPT FOLLOW asFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow) if !ok { diff --git a/internal/federation/federatingdb/undo.go b/internal/federation/federatingdb/undo.go index 7b49815df..481c2d787 100644 --- a/internal/federation/federatingdb/undo.go +++ b/internal/federation/federatingdb/undo.go @@ -73,7 +73,7 @@ func (f *federatingDB) Undo(ctx context.Context, undo vocab.ActivityStreamsUndo) continue } switch iter.GetType().GetTypeName() { - case string(ap.ActivityFollow): + case ap.ActivityFollow: // UNDO FOLLOW ASFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow) if !ok { @@ -102,11 +102,11 @@ func (f *federatingDB) Undo(ctx context.Context, undo vocab.ActivityStreamsUndo) } l.Debug("follow undone") return nil - case string(ap.ActivityLike): + case ap.ActivityLike: // UNDO LIKE - case string(ap.ActivityAnnounce): + case ap.ActivityAnnounce: // UNDO BOOST/REBLOG/ANNOUNCE - case string(ap.ActivityBlock): + case ap.ActivityBlock: // UNDO BLOCK ASBlock, ok := iter.GetType().(vocab.ActivityStreamsBlock) if !ok { diff --git a/internal/gtsmodel/application.go b/internal/gtsmodel/application.go index 27ea61000..7ec71ec2e 100644 --- a/internal/gtsmodel/application.go +++ b/internal/gtsmodel/application.go @@ -29,7 +29,7 @@ type Application struct { Name string `validate:"required" bun:",nullzero,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:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the associated oauth client entity in the db + 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:"-" bun:",nullzero,notnull,default:'read'"` // scopes requested when this app was created + Scopes string `validate:"required" bun:",nullzero,notnull"` // scopes requested when this app was created } diff --git a/internal/gtsmodel/token.go b/internal/gtsmodel/token.go index 4188ea9eb..5fa96e915 100644 --- a/internal/gtsmodel/token.go +++ b/internal/gtsmodel/token.go @@ -28,7 +28,7 @@ type Token struct { 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,notnull"` // 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:"omitempty" bun:",nullzero,notnull,default:'read'"` // Oauth scope + Scope string `validate:"required" bun:",nullzero,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 diff --git a/internal/gtsmodel/user.go b/internal/gtsmodel/user.go index 70f328619..28156cfdd 100644 --- a/internal/gtsmodel/user.go +++ b/internal/gtsmodel/user.go @@ -38,7 +38,7 @@ type User struct { CurrentSignInIP net.IP `validate:"-" bun:",nullzero"` // What's the most recent IP of this user LastSignInAt time.Time `validate:"-" bun:"type:timestamp,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:"-" bun:",nullzero,notnull,default:0"` // How many times has this user signed in? + SignInCount int `validate:"min=0" bun:",nullzero,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? diff --git a/internal/validate/application_test.go b/internal/validate/application_test.go index 38fe0d8ff..3e4dc4235 100644 --- a/internal/validate/application_test.go +++ b/internal/validate/application_test.go @@ -48,7 +48,7 @@ type ApplicationValidateTestSuite struct { func (suite *ApplicationValidateTestSuite) TestValidateApplicationHappyPath() { // no problem here a := happyApplication() - err := validate.Struct(*a) + err := validate.Struct(a) suite.NoError(err) } @@ -56,11 +56,11 @@ func (suite *ApplicationValidateTestSuite) TestValidateApplicationBadID() { a := happyApplication() a.ID = "" - err := validate.Struct(*a) + 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) + err = validate.Struct(a) suite.EqualError(err, "Key: 'Application.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") } @@ -68,7 +68,7 @@ func (suite *ApplicationValidateTestSuite) TestValidateApplicationNoCreatedAt() a := happyApplication() a.CreatedAt = time.Time{} - err := validate.Struct(*a) + err := validate.Struct(a) suite.NoError(err) } @@ -76,7 +76,7 @@ func (suite *ApplicationValidateTestSuite) TestValidateApplicationName() { a := happyApplication() a.Name = "" - err := validate.Struct(*a) + err := validate.Struct(a) suite.EqualError(err, "Key: 'Application.Name' Error:Field validation for 'Name' failed on the 'required' tag") } @@ -84,11 +84,11 @@ func (suite *ApplicationValidateTestSuite) TestValidateApplicationWebsite() { a := happyApplication() a.Website = "invalid-website" - err := validate.Struct(*a) + 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) + err = validate.Struct(a) suite.NoError(err) } @@ -96,15 +96,15 @@ func (suite *ApplicationValidateTestSuite) TestValidateApplicationRedirectURI() a := happyApplication() a.RedirectURI = "invalid-uri" - err := validate.Struct(*a) + 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) + 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) + err = validate.Struct(a) suite.NoError(err) } @@ -112,11 +112,11 @@ func (suite *ApplicationValidateTestSuite) TestValidateApplicationClientSecret() a := happyApplication() a.ClientSecret = "invalid-uuid" - err := validate.Struct(*a) + 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) + err = validate.Struct(a) suite.EqualError(err, "Key: 'Application.ClientSecret' Error:Field validation for 'ClientSecret' failed on the 'required' tag") } @@ -124,8 +124,8 @@ func (suite *ApplicationValidateTestSuite) TestValidateApplicationScopes() { a := happyApplication() a.Scopes = "" - err := validate.Struct(*a) - suite.NoError(err) + 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) { diff --git a/internal/validate/block_test.go b/internal/validate/block_test.go index 0b3293fb5..6522e217b 100644 --- a/internal/validate/block_test.go +++ b/internal/validate/block_test.go @@ -46,68 +46,68 @@ type BlockValidateTestSuite struct { func (suite *BlockValidateTestSuite) TestValidateBlockHappyPath() { // no problem here - d := happyBlock() - err := validate.Struct(*d) + b := happyBlock() + err := validate.Struct(b) suite.NoError(err) } func (suite *BlockValidateTestSuite) TestValidateBlockBadID() { - d := happyBlock() + b := happyBlock() - d.ID = "" - err := validate.Struct(*d) + b.ID = "" + err := validate.Struct(b) suite.EqualError(err, "Key: 'Block.ID' Error:Field validation for 'ID' failed on the 'required' tag") - d.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(*d) + 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() { - d := happyBlock() + b := happyBlock() - d.CreatedAt = time.Time{} - err := validate.Struct(*d) + b.CreatedAt = time.Time{} + err := validate.Struct(b) suite.NoError(err) } func (suite *BlockValidateTestSuite) TestValidateBlockCreatedByAccountID() { - d := happyBlock() + b := happyBlock() - d.AccountID = "" - err := validate.Struct(*d) + b.AccountID = "" + err := validate.Struct(b) suite.EqualError(err, "Key: 'Block.AccountID' Error:Field validation for 'AccountID' failed on the 'required' tag") - d.AccountID = "this-is-not-a-valid-ulid" - err = validate.Struct(*d) + 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() { - d := happyBlock() + b := happyBlock() - d.TargetAccountID = "invalid-ulid" - err := validate.Struct(*d) + b.TargetAccountID = "invalid-ulid" + err := validate.Struct(b) suite.EqualError(err, "Key: 'Block.TargetAccountID' Error:Field validation for 'TargetAccountID' failed on the 'ulid' tag") - d.TargetAccountID = "01FEEDHX4G7EGHF5GD9E82Y51Q" - err = validate.Struct(*d) + b.TargetAccountID = "01FEEDHX4G7EGHF5GD9E82Y51Q" + err = validate.Struct(b) suite.NoError(err) - d.TargetAccountID = "" - err = validate.Struct(*d) + 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() { - d := happyBlock() + b := happyBlock() - d.URI = "invalid-uri" - err := validate.Struct(*d) + b.URI = "invalid-uri" + err := validate.Struct(b) suite.EqualError(err, "Key: 'Block.URI' Error:Field validation for 'URI' failed on the 'url' tag") - d.URI = "" - err = validate.Struct(*d) + b.URI = "" + err = validate.Struct(b) suite.EqualError(err, "Key: 'Block.URI' Error:Field validation for 'URI' failed on the 'required' tag") } diff --git a/internal/validate/client_test.go b/internal/validate/client_test.go index d3c2466cf..23deb3c39 100644 --- a/internal/validate/client_test.go +++ b/internal/validate/client_test.go @@ -45,7 +45,7 @@ type ClientValidateTestSuite struct { func (suite *ClientValidateTestSuite) TestValidateClientHappyPath() { // no problem here c := happyClient() - err := validate.Struct(*c) + err := validate.Struct(c) suite.NoError(err) } @@ -53,11 +53,11 @@ func (suite *ClientValidateTestSuite) TestValidateClientBadID() { c := happyClient() c.ID = "" - err := validate.Struct(*c) + 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) + err = validate.Struct(c) suite.EqualError(err, "Key: 'Client.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") } @@ -65,7 +65,7 @@ func (suite *ClientValidateTestSuite) TestValidateClientNoCreatedAt() { c := happyClient() c.CreatedAt = time.Time{} - err := validate.Struct(*c) + err := validate.Struct(c) suite.NoError(err) } @@ -73,27 +73,27 @@ func (suite *ClientValidateTestSuite) TestValidateClientDomain() { c := happyClient() c.Domain = "invalid-uri" - err := validate.Struct(*c) + 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) + 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) + err = validate.Struct(c) suite.NoError(err) } func (suite *ClientValidateTestSuite) TestValidateSecret() { - a := happyClient() + c := happyClient() - a.Secret = "invalid-uuid" - err := validate.Struct(*a) + c.Secret = "invalid-uuid" + err := validate.Struct(c) suite.EqualError(err, "Key: 'Client.Secret' Error:Field validation for 'Secret' failed on the 'uuid' tag") - a.Secret = "" - err = validate.Struct(*a) + c.Secret = "" + err = validate.Struct(c) suite.EqualError(err, "Key: 'Client.Secret' Error:Field validation for 'Secret' failed on the 'required' tag") } diff --git a/internal/validate/domainblock_test.go b/internal/validate/domainblock_test.go index 0ce826f12..4a0777677 100644 --- a/internal/validate/domainblock_test.go +++ b/internal/validate/domainblock_test.go @@ -48,7 +48,7 @@ type DomainBlockValidateTestSuite struct { func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockHappyPath() { // no problem here d := happyDomainBlock() - err := validate.Struct(*d) + err := validate.Struct(d) suite.NoError(err) } @@ -56,11 +56,11 @@ func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockBadID() { d := happyDomainBlock() d.ID = "" - err := validate.Struct(*d) + 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) + err = validate.Struct(d) suite.EqualError(err, "Key: 'DomainBlock.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") } @@ -68,7 +68,7 @@ func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockNoCreatedAt() d := happyDomainBlock() d.CreatedAt = time.Time{} - err := validate.Struct(*d) + err := validate.Struct(d) suite.NoError(err) } @@ -76,11 +76,11 @@ func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockBadDomain() { d := happyDomainBlock() d.Domain = "" - err := validate.Struct(*d) + 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) + err = validate.Struct(d) suite.EqualError(err, "Key: 'DomainBlock.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag") } @@ -88,11 +88,11 @@ func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockCreatedByAccou d := happyDomainBlock() d.CreatedByAccountID = "" - err := validate.Struct(*d) + 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) + err = validate.Struct(d) suite.EqualError(err, "Key: 'DomainBlock.CreatedByAccountID' Error:Field validation for 'CreatedByAccountID' failed on the 'ulid' tag") } @@ -101,7 +101,7 @@ func (suite *DomainBlockValidateTestSuite) TestValidateDomainBlockComments() { d.PrivateComment = "" d.PublicComment = "" - err := validate.Struct(*d) + err := validate.Struct(d) suite.NoError(err) } @@ -109,11 +109,11 @@ func (suite *DomainBlockValidateTestSuite) TestValidateDomainSubscriptionID() { d := happyDomainBlock() d.SubscriptionID = "invalid-ulid" - err := validate.Struct(*d) + 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) + err = validate.Struct(d) suite.NoError(err) } diff --git a/internal/validate/emaildomainblock_test.go b/internal/validate/emaildomainblock_test.go index 26ba08615..04e81ad86 100644 --- a/internal/validate/emaildomainblock_test.go +++ b/internal/validate/emaildomainblock_test.go @@ -44,7 +44,7 @@ type EmailDomainBlockValidateTestSuite struct { func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockHappyPath() { // no problem here e := happyEmailDomainBlock() - err := validate.Struct(*e) + err := validate.Struct(e) suite.NoError(err) } @@ -52,11 +52,11 @@ func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockBadI e := happyEmailDomainBlock() e.ID = "" - err := validate.Struct(*e) + 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) + err = validate.Struct(e) suite.EqualError(err, "Key: 'EmailDomainBlock.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") } @@ -64,7 +64,7 @@ func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockNoCr e := happyEmailDomainBlock() e.CreatedAt = time.Time{} - err := validate.Struct(*e) + err := validate.Struct(e) suite.NoError(err) } @@ -72,11 +72,11 @@ func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockBadD e := happyEmailDomainBlock() e.Domain = "" - err := validate.Struct(*e) + 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) + err = validate.Struct(e) suite.EqualError(err, "Key: 'EmailDomainBlock.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag") } @@ -84,11 +84,11 @@ func (suite *EmailDomainBlockValidateTestSuite) TestValidateEmailDomainBlockCrea e := happyEmailDomainBlock() e.CreatedByAccountID = "" - err := validate.Struct(*e) + 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) + err = validate.Struct(e) suite.EqualError(err, "Key: 'EmailDomainBlock.CreatedByAccountID' Error:Field validation for 'CreatedByAccountID' failed on the 'ulid' tag") } diff --git a/internal/validate/emoji_test.go b/internal/validate/emoji_test.go index 4ada1ca8b..9c42b7363 100644 --- a/internal/validate/emoji_test.go +++ b/internal/validate/emoji_test.go @@ -95,27 +95,27 @@ func (suite *EmojiValidateTestSuite) TestValidateEmojiBadFilePaths() { e := happyEmoji() e.ImagePath = "/tmp/nonexistent/file/for/gotosocial/test" - err := validate.Struct(*e) + 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) + 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) + 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) + 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) + 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) + 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") } @@ -123,11 +123,11 @@ func (suite *EmojiValidateTestSuite) TestValidateEmojiURI() { e := happyEmoji() e.URI = "aaaaaaaaaa" - err := validate.Struct(*e) + 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) + err = validate.Struct(e) suite.EqualError(err, "Key: 'Emoji.URI' Error:Field validation for 'URI' failed on the 'url' tag") } @@ -135,26 +135,26 @@ func (suite *EmojiValidateTestSuite) TestValidateEmojiURLCombos() { e := happyEmoji() e.ImageRemoteURL = "" - err := validate.Struct(*e) + 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) + err = validate.Struct(e) suite.NoError(err) e.ImageStaticRemoteURL = "" - err = validate.Struct(*e) + 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) + err = validate.Struct(e) suite.NoError(err) e.ImageURL = "" e.ImageStaticURL = "" e.ImageRemoteURL = "" e.ImageStaticRemoteURL = "" - err = validate.Struct(*e) + 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") } @@ -162,19 +162,19 @@ func (suite *EmojiValidateTestSuite) TestValidateFileSize() { e := happyEmoji() e.ImageFileSize = 0 - err := validate.Struct(*e) + 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) + 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) + 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) + 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") } @@ -182,11 +182,11 @@ func (suite *EmojiValidateTestSuite) TestValidateDomain() { e := happyEmoji() e.Domain = "" - err := validate.Struct(*e) + 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) + err = validate.Struct(e) suite.EqualError(err, "Key: 'Emoji.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag") } diff --git a/internal/validate/follow_test.go b/internal/validate/follow_test.go index d624666be..840f805bf 100644 --- a/internal/validate/follow_test.go +++ b/internal/validate/follow_test.go @@ -47,7 +47,7 @@ type FollowValidateTestSuite struct { func (suite *FollowValidateTestSuite) TestValidateFollowHappyPath() { // no problem here f := happyFollow() - err := validate.Struct(*f) + err := validate.Struct(f) suite.NoError(err) } @@ -55,11 +55,11 @@ func (suite *FollowValidateTestSuite) TestValidateFollowBadID() { f := happyFollow() f.ID = "" - err := validate.Struct(*f) + 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) + err = validate.Struct(f) suite.EqualError(err, "Key: 'Follow.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") } @@ -67,7 +67,7 @@ func (suite *FollowValidateTestSuite) TestValidateFollowNoCreatedAt() { f := happyFollow() f.CreatedAt = time.Time{} - err := validate.Struct(*f) + err := validate.Struct(f) suite.NoError(err) } @@ -75,11 +75,11 @@ func (suite *FollowValidateTestSuite) TestValidateFollowNoURI() { f := happyFollow() f.URI = "" - err := validate.Struct(*f) + 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) + err = validate.Struct(f) suite.EqualError(err, "Key: 'Follow.URI' Error:Field validation for 'URI' failed on the 'url' tag") } diff --git a/internal/validate/followrequest_test.go b/internal/validate/followrequest_test.go index f607e24c3..24744fb53 100644 --- a/internal/validate/followrequest_test.go +++ b/internal/validate/followrequest_test.go @@ -47,7 +47,7 @@ type FollowRequestValidateTestSuite struct { func (suite *FollowRequestValidateTestSuite) TestValidateFollowRequestHappyPath() { // no problem here f := happyFollowRequest() - err := validate.Struct(*f) + err := validate.Struct(f) suite.NoError(err) } @@ -55,11 +55,11 @@ func (suite *FollowRequestValidateTestSuite) TestValidateFollowRequestBadID() { f := happyFollowRequest() f.ID = "" - err := validate.Struct(*f) + 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) + err = validate.Struct(f) suite.EqualError(err, "Key: 'FollowRequest.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") } @@ -67,7 +67,7 @@ func (suite *FollowRequestValidateTestSuite) TestValidateFollowRequestNoCreatedA f := happyFollowRequest() f.CreatedAt = time.Time{} - err := validate.Struct(*f) + err := validate.Struct(f) suite.NoError(err) } @@ -75,11 +75,11 @@ func (suite *FollowRequestValidateTestSuite) TestValidateFollowRequestNoURI() { f := happyFollowRequest() f.URI = "" - err := validate.Struct(*f) + 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) + err = validate.Struct(f) suite.EqualError(err, "Key: 'FollowRequest.URI' Error:Field validation for 'URI' failed on the 'url' tag") } diff --git a/internal/validate/instance_test.go b/internal/validate/instance_test.go index 6042da80e..e2b569363 100644 --- a/internal/validate/instance_test.go +++ b/internal/validate/instance_test.go @@ -77,11 +77,11 @@ func (suite *InstanceValidateTestSuite) TestValidateInstanceAccountURI() { i := happyInstance() i.URI = "" - err := validate.Struct(*i) + 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) + err = validate.Struct(i) suite.EqualError(err, "Key: 'Instance.URI' Error:Field validation for 'URI' failed on the 'url' tag") } @@ -89,19 +89,19 @@ func (suite *InstanceValidateTestSuite) TestValidateInstanceDodgyAccountID() { i := happyInstance() i.ContactAccountID = "9HZJ76B6VXSKF" - err := validate.Struct(*i) + 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) + 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) + 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) + err = validate.Struct(i) suite.NoError(err) } @@ -109,15 +109,15 @@ func (suite *InstanceValidateTestSuite) TestValidateInstanceDomain() { i := happyInstance() i.Domain = "poopoo" - err := validate.Struct(*i) + 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) + 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) + err = validate.Struct(i) suite.EqualError(err, "Key: 'Instance.Domain' Error:Field validation for 'Domain' failed on the 'fqdn' tag") } @@ -125,11 +125,11 @@ func (suite *InstanceValidateTestSuite) TestValidateInstanceContactEmail() { i := happyInstance() i.ContactEmail = "poopoo" - err := validate.Struct(*i) + 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) + err = validate.Struct(i) suite.NoError(err) } @@ -137,7 +137,7 @@ func (suite *InstanceValidateTestSuite) TestValidateInstanceNoCreatedAt() { i := happyInstance() i.CreatedAt = time.Time{} - err := validate.Struct(*i) + err := validate.Struct(i) suite.NoError(err) } diff --git a/internal/validate/mediaattachment_test.go b/internal/validate/mediaattachment_test.go index 84c2fa4eb..58108fc42 100644 --- a/internal/validate/mediaattachment_test.go +++ b/internal/validate/mediaattachment_test.go @@ -109,7 +109,7 @@ type MediaAttachmentValidateTestSuite struct { func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentHappyPath() { // no problem here m := happyMediaAttachment() - err := validate.Struct(*m) + err := validate.Struct(m) suite.NoError(err) } @@ -117,27 +117,27 @@ func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBadFil m := happyMediaAttachment() m.File.Path = "/tmp/nonexistent/file/for/gotosocial/test" - err := validate.Struct(*m) + 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) + 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) + 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) + 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) + 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) + 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") } @@ -145,11 +145,11 @@ func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBadTyp m := happyMediaAttachment() m.Type = "" - err := validate.Struct(*m) + 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) + err = validate.Struct(m) suite.EqualError(err, "Key: 'MediaAttachment.Type' Error:Field validation for 'Type' failed on the 'oneof' tag") } @@ -157,23 +157,23 @@ func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBadFil m := happyMediaAttachment() m.FileMeta.Original.Aspect = 0 - err := validate.Struct(*m) + 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) + 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) + err = validate.Struct(m) suite.NoError(err) m.FileMeta.Focus.X = 3.6 - err = validate.Struct(*m) + 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) + 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") } @@ -181,19 +181,19 @@ func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBadURL m := happyMediaAttachment() m.URL = "aaaaaaaaaa" - err := validate.Struct(*m) + 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) + 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) + 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) + err = validate.Struct(m) suite.NoError(err) } @@ -201,15 +201,15 @@ func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentBlurha m := happyMediaAttachment() m.Blurhash = "" - err := validate.Struct(*m) + 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) + err = validate.Struct(m) suite.NoError(err) m.Blurhash = "some_blurhash" - err = validate.Struct(*m) + err = validate.Struct(m) suite.NoError(err) } @@ -217,11 +217,11 @@ func (suite *MediaAttachmentValidateTestSuite) TestValidateMediaAttachmentProces m := happyMediaAttachment() m.Processing = 420 - err := validate.Struct(*m) + 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) + err = validate.Struct(m) suite.EqualError(err, "Key: 'MediaAttachment.Processing' Error:Field validation for 'Processing' failed on the 'oneof' tag") } diff --git a/internal/validate/mention_test.go b/internal/validate/mention_test.go index 59ba1e3fd..1b460803c 100644 --- a/internal/validate/mention_test.go +++ b/internal/validate/mention_test.go @@ -49,7 +49,7 @@ type MentionValidateTestSuite struct { func (suite *MentionValidateTestSuite) TestValidateMentionHappyPath() { // no problem here m := happyMention() - err := validate.Struct(*m) + err := validate.Struct(m) suite.NoError(err) } @@ -57,11 +57,11 @@ func (suite *MentionValidateTestSuite) TestValidateMentionBadID() { m := happyMention() m.ID = "" - err := validate.Struct(*m) + 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) + err = validate.Struct(m) suite.EqualError(err, "Key: 'Mention.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") } @@ -69,11 +69,11 @@ func (suite *MentionValidateTestSuite) TestValidateMentionAccountURI() { m := happyMention() m.OriginAccountURI = "" - err := validate.Struct(*m) + 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) + err = validate.Struct(m) suite.EqualError(err, "Key: 'Mention.OriginAccountURI' Error:Field validation for 'OriginAccountURI' failed on the 'url' tag") } @@ -81,11 +81,11 @@ func (suite *MentionValidateTestSuite) TestValidateMentionDodgyStatusID() { m := happyMention() m.StatusID = "9HZJ76B6VXSKF" - err := validate.Struct(*m) + 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) + err = validate.Struct(m) suite.EqualError(err, "Key: 'Mention.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") } @@ -93,7 +93,7 @@ func (suite *MentionValidateTestSuite) TestValidateMentionNoCreatedAt() { m := happyMention() m.CreatedAt = time.Time{} - err := validate.Struct(*m) + err := validate.Struct(m) suite.NoError(err) } diff --git a/internal/validate/notification_test.go b/internal/validate/notification_test.go index 700560f2a..bca1ac9fe 100644 --- a/internal/validate/notification_test.go +++ b/internal/validate/notification_test.go @@ -47,49 +47,49 @@ type NotificationValidateTestSuite struct { func (suite *NotificationValidateTestSuite) TestValidateNotificationHappyPath() { // no problem here - m := happyNotification() - err := validate.Struct(*m) + n := happyNotification() + err := validate.Struct(n) suite.NoError(err) } func (suite *NotificationValidateTestSuite) TestValidateNotificationBadID() { - m := happyNotification() + n := happyNotification() - m.ID = "" - err := validate.Struct(*m) + n.ID = "" + err := validate.Struct(n) suite.EqualError(err, "Key: 'Notification.ID' Error:Field validation for 'ID' failed on the 'required' tag") - m.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(*m) + 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() { - m := happyNotification() + n := happyNotification() - m.StatusID = "" - err := validate.Struct(*m) + n.StatusID = "" + err := validate.Struct(n) suite.EqualError(err, "Key: 'Notification.StatusID' Error:Field validation for 'StatusID' failed on the 'required_if' tag") - m.StatusID = "9HZJ76B6VXSKF" - err = validate.Struct(*m) + n.StatusID = "9HZJ76B6VXSKF" + err = validate.Struct(n) suite.EqualError(err, "Key: 'Notification.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") - m.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!" - err = validate.Struct(*m) + n.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!" + err = validate.Struct(n) suite.EqualError(err, "Key: 'Notification.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") - m.StatusID = "" - m.NotificationType = gtsmodel.NotificationFollowRequest - err = validate.Struct(*m) + n.StatusID = "" + n.NotificationType = gtsmodel.NotificationFollowRequest + err = validate.Struct(n) suite.NoError(err) } func (suite *NotificationValidateTestSuite) TestValidateNotificationNoCreatedAt() { - m := happyNotification() + n := happyNotification() - m.CreatedAt = time.Time{} - err := validate.Struct(*m) + n.CreatedAt = time.Time{} + err := validate.Struct(n) suite.NoError(err) } diff --git a/internal/validate/routersession_test.go b/internal/validate/routersession_test.go index 7ab4034d1..8f4e112a5 100644 --- a/internal/validate/routersession_test.go +++ b/internal/validate/routersession_test.go @@ -41,7 +41,7 @@ type RouterSessionValidateTestSuite struct { func (suite *RouterSessionValidateTestSuite) TestValidateRouterSessionHappyPath() { // no problem here r := happyRouterSession() - err := validate.Struct(*r) + err := validate.Struct(r) suite.NoError(err) } @@ -50,17 +50,17 @@ func (suite *RouterSessionValidateTestSuite) TestValidateRouterSessionAuth() { // remove auth struct r.Auth = nil - err := validate.Struct(*r) + 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) + 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) + err = validate.Struct(r) suite.EqualError(err, "Key: 'RouterSession.Auth' Error:Field validation for 'Auth' failed on the 'len' tag") } @@ -69,17 +69,17 @@ func (suite *RouterSessionValidateTestSuite) TestValidateRouterSessionCrypt() { // remove crypt struct r.Crypt = nil - err := validate.Struct(*r) + 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) + 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) + err = validate.Struct(r) suite.EqualError(err, "Key: 'RouterSession.Crypt' Error:Field validation for 'Crypt' failed on the 'len' tag") } diff --git a/internal/validate/status_test.go b/internal/validate/status_test.go index 57e7259b9..7c85414b1 100644 --- a/internal/validate/status_test.go +++ b/internal/validate/status_test.go @@ -82,7 +82,7 @@ type StatusValidateTestSuite struct { func (suite *StatusValidateTestSuite) TestValidateStatusHappyPath() { // no problem here s := happyStatus() - err := validate.Struct(*s) + err := validate.Struct(s) suite.NoError(err) } @@ -90,11 +90,11 @@ func (suite *StatusValidateTestSuite) TestValidateStatusBadID() { s := happyStatus() s.ID = "" - err := validate.Struct(*s) + 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) + err = validate.Struct(s) suite.EqualError(err, "Key: 'Status.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") } @@ -102,23 +102,23 @@ func (suite *StatusValidateTestSuite) TestValidateStatusAttachmentIDs() { s := happyStatus() s.AttachmentIDs[0] = "" - err := validate.Struct(*s) + 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) + 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) + 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) + err = validate.Struct(s) suite.NoError(err) s.AttachmentIDs = nil - err = validate.Struct(*s) + err = validate.Struct(s) suite.NoError(err) } @@ -126,11 +126,11 @@ func (suite *StatusValidateTestSuite) TestStatusApplicationID() { s := happyStatus() s.CreatedWithApplicationID = "" - err := validate.Struct(*s) + err := validate.Struct(s) suite.EqualError(err, "Key: 'Status.CreatedWithApplicationID' Error:Field validation for 'CreatedWithApplicationID' failed on the 'required_if' tag") s.Local = false - err = validate.Struct(*s) + err = validate.Struct(s) suite.NoError(err) } @@ -138,23 +138,23 @@ func (suite *StatusValidateTestSuite) TestValidateStatusReplyFields() { s := happyStatus() s.InReplyToAccountID = "01FEBCTP6DN7961PN81C3DVM4N " - err := validate.Struct(*s) + 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) + 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) + 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) + 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) + err = validate.Struct(s) suite.NoError(err) } diff --git a/internal/validate/statusbookmark_test.go b/internal/validate/statusbookmark_test.go index 667994aa0..a52d0e26a 100644 --- a/internal/validate/statusbookmark_test.go +++ b/internal/validate/statusbookmark_test.go @@ -46,40 +46,40 @@ type StatusBookmarkValidateTestSuite struct { func (suite *StatusBookmarkValidateTestSuite) TestValidateStatusBookmarkHappyPath() { // no problem here - m := happyStatusBookmark() - err := validate.Struct(*m) + s := happyStatusBookmark() + err := validate.Struct(s) suite.NoError(err) } func (suite *StatusBookmarkValidateTestSuite) TestValidateStatusBookmarkBadID() { - m := happyStatusBookmark() + s := happyStatusBookmark() - m.ID = "" - err := validate.Struct(*m) + s.ID = "" + err := validate.Struct(s) suite.EqualError(err, "Key: 'StatusBookmark.ID' Error:Field validation for 'ID' failed on the 'required' tag") - m.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(*m) + 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() { - m := happyStatusBookmark() + s := happyStatusBookmark() - m.StatusID = "9HZJ76B6VXSKF" - err := validate.Struct(*m) + s.StatusID = "9HZJ76B6VXSKF" + err := validate.Struct(s) suite.EqualError(err, "Key: 'StatusBookmark.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") - m.StatusID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!!!!!!!!!!!" - err = validate.Struct(*m) + 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() { - m := happyStatusBookmark() + s := happyStatusBookmark() - m.CreatedAt = time.Time{} - err := validate.Struct(*m) + s.CreatedAt = time.Time{} + err := validate.Struct(s) suite.NoError(err) } diff --git a/internal/validate/statusfave_test.go b/internal/validate/statusfave_test.go index 52bef1749..18808f51e 100644 --- a/internal/validate/statusfave_test.go +++ b/internal/validate/statusfave_test.go @@ -48,7 +48,7 @@ type StatusFaveValidateTestSuite struct { func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveHappyPath() { // no problem here f := happyStatusFave() - err := validate.Struct(*f) + err := validate.Struct(f) suite.NoError(err) } @@ -56,11 +56,11 @@ func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveBadID() { f := happyStatusFave() f.ID = "" - err := validate.Struct(*f) + 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) + err = validate.Struct(f) suite.EqualError(err, "Key: 'StatusFave.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") } @@ -68,11 +68,11 @@ func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveDodgyStatusID() f := happyStatusFave() f.StatusID = "9HZJ76B6VXSKF" - err := validate.Struct(*f) + 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) + err = validate.Struct(f) suite.EqualError(err, "Key: 'StatusFave.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") } @@ -80,7 +80,7 @@ func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveNoCreatedAt() { f := happyStatusFave() f.CreatedAt = time.Time{} - err := validate.Struct(*f) + err := validate.Struct(f) suite.NoError(err) } @@ -88,11 +88,11 @@ func (suite *StatusFaveValidateTestSuite) TestValidateStatusFaveNoURI() { f := happyStatusFave() f.URI = "" - err := validate.Struct(*f) + 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) + err = validate.Struct(f) suite.EqualError(err, "Key: 'StatusFave.URI' Error:Field validation for 'URI' failed on the 'url' tag") } diff --git a/internal/validate/statusmute_test.go b/internal/validate/statusmute_test.go index de23114eb..c3fe5129d 100644 --- a/internal/validate/statusmute_test.go +++ b/internal/validate/statusmute_test.go @@ -47,7 +47,7 @@ type StatusMuteValidateTestSuite struct { func (suite *StatusMuteValidateTestSuite) TestValidateStatusMuteHappyPath() { // no problem here m := happyStatusMute() - err := validate.Struct(*m) + err := validate.Struct(m) suite.NoError(err) } @@ -55,11 +55,11 @@ func (suite *StatusMuteValidateTestSuite) TestValidateStatusMuteBadID() { m := happyStatusMute() m.ID = "" - err := validate.Struct(*m) + 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) + err = validate.Struct(m) suite.EqualError(err, "Key: 'StatusMute.ID' Error:Field validation for 'ID' failed on the 'ulid' tag") } @@ -67,11 +67,11 @@ func (suite *StatusMuteValidateTestSuite) TestValidateStatusMuteDodgyStatusID() m := happyStatusMute() m.StatusID = "9HZJ76B6VXSKF" - err := validate.Struct(*m) + 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) + err = validate.Struct(m) suite.EqualError(err, "Key: 'StatusMute.StatusID' Error:Field validation for 'StatusID' failed on the 'ulid' tag") } @@ -79,7 +79,7 @@ func (suite *StatusMuteValidateTestSuite) TestValidateStatusMuteNoCreatedAt() { m := happyStatusMute() m.CreatedAt = time.Time{} - err := validate.Struct(*m) + err := validate.Struct(m) suite.NoError(err) } diff --git a/internal/validate/structvalidation.go b/internal/validate/structvalidation.go index 3c7a4abd9..0111ba1e9 100644 --- a/internal/validate/structvalidation.go +++ b/internal/validate/structvalidation.go @@ -29,7 +29,6 @@ var v *validator.Validate // Validation Panic messages const ( - PointerPanic = "validate function was passed pointer" InvalidPanic = "validate function was passed invalid item" ) @@ -53,11 +52,9 @@ func init() { // Struct validates the passed struct, returning validator.ValidationErrors if invalid, or nil if OK. func Struct(s interface{}) error { - switch reflect.ValueOf(s).Kind() { + switch reflect.TypeOf(s).Kind() { case reflect.Invalid: panic(InvalidPanic) - case reflect.Ptr: - panic(PointerPanic) } err := v.Struct(s) diff --git a/internal/validate/structvalidation_test.go b/internal/validate/structvalidation_test.go index 606d92da4..1942a44f8 100644 --- a/internal/validate/structvalidation_test.go +++ b/internal/validate/structvalidation_test.go @@ -30,15 +30,21 @@ type ValidateTestSuite struct { suite.Suite } -func (suite *ValidateTestSuite) TestValidatePointer() { +func (suite *ValidateTestSuite) TestValidateNilPointer() { var nilUser *gtsmodel.User - suite.PanicsWithValue(validate.PointerPanic, func() { + 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.PanicsWithValue(validate.InvalidPanic, func() { + suite.Panics(func() { validate.Struct(nil) }) } diff --git a/internal/validate/tag_test.go b/internal/validate/tag_test.go index 4f8ab2fba..14032319e 100644 --- a/internal/validate/tag_test.go +++ b/internal/validate/tag_test.go @@ -48,7 +48,7 @@ type TagValidateTestSuite struct { func (suite *TagValidateTestSuite) TestValidateTagHappyPath() { // no problem here t := happyTag() - err := validate.Struct(*t) + err := validate.Struct(t) suite.NoError(err) } @@ -56,7 +56,7 @@ func (suite *TagValidateTestSuite) TestValidateTagNoName() { t := happyTag() t.Name = "" - err := validate.Struct(*t) + err := validate.Struct(t) suite.EqualError(err, "Key: 'Tag.Name' Error:Field validation for 'Name' failed on the 'required' tag") } @@ -64,19 +64,19 @@ func (suite *TagValidateTestSuite) TestValidateTagBadURL() { t := happyTag() t.URL = "" - err := validate.Struct(*t) + err := validate.Struct(t) suite.EqualError(err, "Key: 'Tag.URL' Error:Field validation for 'URL' failed on the 'required' tag") t.URL = "no-schema.com" - err = validate.Struct(*t) + err = validate.Struct(t) suite.EqualError(err, "Key: 'Tag.URL' Error:Field validation for 'URL' failed on the 'url' tag") t.URL = "justastring" - err = validate.Struct(*t) + err = validate.Struct(t) suite.EqualError(err, "Key: 'Tag.URL' Error:Field validation for 'URL' failed on the 'url' tag") t.URL = "https://aaa\n\n\naaaaaaaa" - err = validate.Struct(*t) + err = validate.Struct(t) suite.EqualError(err, "Key: 'Tag.URL' Error:Field validation for 'URL' failed on the 'url' tag") } @@ -84,7 +84,7 @@ func (suite *TagValidateTestSuite) TestValidateTagNoFirstSeenFromAccountID() { t := happyTag() t.FirstSeenFromAccountID = "" - err := validate.Struct(*t) + err := validate.Struct(t) suite.NoError(err) } diff --git a/internal/validate/token_test.go b/internal/validate/token_test.go index bf12328a2..cf4c8a6ca 100644 --- a/internal/validate/token_test.go +++ b/internal/validate/token_test.go @@ -45,53 +45,53 @@ type TokenValidateTestSuite struct { func (suite *TokenValidateTestSuite) TestValidateTokenHappyPath() { // no problem here - a := happyToken() - err := validate.Struct(*a) + t := happyToken() + err := validate.Struct(t) suite.NoError(err) } func (suite *TokenValidateTestSuite) TestValidateTokenBadID() { - a := happyToken() + t := happyToken() - a.ID = "" - err := validate.Struct(*a) + t.ID = "" + err := validate.Struct(t) suite.EqualError(err, "Key: 'Token.ID' Error:Field validation for 'ID' failed on the 'required' tag") - a.ID = "01FE96W293ZPRG9FQQP48HK8N001FE96W32AT24VYBGM12WN3GKB" - err = validate.Struct(*a) + 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() { - a := happyToken() + t := happyToken() - a.CreatedAt = time.Time{} - err := validate.Struct(*a) + t.CreatedAt = time.Time{} + err := validate.Struct(t) suite.NoError(err) } func (suite *TokenValidateTestSuite) TestValidateTokenRedirectURI() { - a := happyToken() + t := happyToken() - a.RedirectURI = "invalid-uri" - err := validate.Struct(*a) + t.RedirectURI = "invalid-uri" + err := validate.Struct(t) suite.EqualError(err, "Key: 'Token.RedirectURI' Error:Field validation for 'RedirectURI' failed on the 'uri' tag") - a.RedirectURI = "" - err = validate.Struct(*a) + t.RedirectURI = "" + err = validate.Struct(t) suite.EqualError(err, "Key: 'Token.RedirectURI' Error:Field validation for 'RedirectURI' failed on the 'required' tag") - a.RedirectURI = "urn:ietf:wg:oauth:2.0:oob" - err = validate.Struct(*a) + t.RedirectURI = "urn:ietf:wg:oauth:2.0:oob" + err = validate.Struct(t) suite.NoError(err) } func (suite *TokenValidateTestSuite) TestValidateTokenScope() { - a := happyToken() + t := happyToken() - a.Scope = "" - err := validate.Struct(*a) - suite.NoError(err) + 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) { diff --git a/internal/validate/user_test.go b/internal/validate/user_test.go index 5a13a510f..f747b7c08 100644 --- a/internal/validate/user_test.go +++ b/internal/validate/user_test.go @@ -68,7 +68,7 @@ type UserValidateTestSuite struct { func (suite *UserValidateTestSuite) TestValidateUserHappyPath() { // no problem here u := happyUser() - err := validate.Struct(*u) + err := validate.Struct(u) suite.NoError(err) } @@ -77,7 +77,7 @@ func (suite *UserValidateTestSuite) TestValidateUserNoID() { u := happyUser() u.ID = "" - err := validate.Struct(*u) + err := validate.Struct(u) suite.EqualError(err, "Key: 'User.ID' Error:Field validation for 'ID' failed on the 'required' tag") } @@ -86,7 +86,7 @@ func (suite *UserValidateTestSuite) TestValidateUserNoEmail() { u := happyUser() u.Email = "" - err := validate.Struct(*u) + 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") } @@ -96,7 +96,7 @@ func (suite *UserValidateTestSuite) TestValidateUserOnlyUnconfirmedEmail() { u.Email = "" u.UnconfirmedEmail = "whatever@example.org" - err := validate.Struct(*u) + err := validate.Struct(u) suite.EqualError(err, "Key: 'User.Email' Error:Field validation for 'Email' failed on the 'required_with' tag") } @@ -107,7 +107,7 @@ func (suite *UserValidateTestSuite) TestValidateUserOnlyUnconfirmedEmailOK() { u.UnconfirmedEmail = "whatever@example.org" u.ConfirmedAt = time.Time{} - err := validate.Struct(*u) + err := validate.Struct(u) suite.NoError(err) } @@ -116,10 +116,19 @@ func (suite *UserValidateTestSuite) TestValidateUserNoConfirmedAt() { u := happyUser() u.ConfirmedAt = time.Time{} - err := validate.Struct(*u) + 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)) } -- cgit v1.3 From 32fe2282c4aafcb060ac5d0a5ccfd5a884eced88 Mon Sep 17 00:00:00 2001 From: tsmethurst Date: Fri, 3 Sep 2021 11:12:19 +0200 Subject: review changes --- internal/validate/structvalidation.go | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'internal/validate') diff --git a/internal/validate/structvalidation.go b/internal/validate/structvalidation.go index 0111ba1e9..ff72e253d 100644 --- a/internal/validate/structvalidation.go +++ b/internal/validate/structvalidation.go @@ -27,11 +27,6 @@ import ( var v *validator.Validate -// Validation Panic messages -const ( - InvalidPanic = "validate function was passed invalid item" -) - func ulidValidator(fl validator.FieldLevel) bool { field := fl.Field() @@ -52,13 +47,7 @@ func init() { // Struct validates the passed struct, returning validator.ValidationErrors if invalid, or nil if OK. func Struct(s interface{}) error { - switch reflect.TypeOf(s).Kind() { - case reflect.Invalid: - panic(InvalidPanic) - } - - err := v.Struct(s) - return processValidationError(err) + return processValidationError(v.Struct(s)) } func processValidationError(err error) error { -- cgit v1.3