Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
31f634b7
Commit
31f634b7
authored
Jan 12, 2026
by
Johnny
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: add more tests
parent
8f9ff563
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
1934 additions
and
1 deletion
+1934
-1
inbox.go
store/db/postgres/inbox.go
+2
-1
activity_test.go
store/test/activity_test.go
+267
-0
idp_test.go
store/test/idp_test.go
+381
-0
inbox_test.go
store/test/inbox_test.go
+525
-0
memo_relation_test.go
store/test/memo_relation_test.go
+429
-0
user_setting_test.go
store/test/user_setting_test.go
+330
-0
No files found.
store/db/postgres/inbox.go
View file @
31f634b7
...
...
@@ -53,7 +53,8 @@ func (d *DB) ListInboxes(ctx context.Context, find *store.FindInbox) ([]*store.I
if
find
.
MessageType
!=
nil
{
// Filter by message type using PostgreSQL JSON extraction
// Note: The type field in JSON is stored as string representation of the enum name
where
,
args
=
append
(
where
,
"message->>'type' = "
+
placeholder
(
len
(
args
)
+
1
)),
append
(
args
,
find
.
MessageType
.
String
())
// Cast to JSONB since the column is TEXT
where
,
args
=
append
(
where
,
"message::JSONB->>'type' = "
+
placeholder
(
len
(
args
)
+
1
)),
append
(
args
,
find
.
MessageType
.
String
())
}
query
:=
"SELECT id, created_ts, sender_id, receiver_id, status, message FROM inbox WHERE "
+
strings
.
Join
(
where
,
" AND "
)
+
" ORDER BY created_ts DESC"
...
...
store/test/activity_test.go
View file @
31f634b7
...
...
@@ -99,3 +99,270 @@ func TestActivityListMultiple(t *testing.T) {
ts
.
Close
()
}
func
TestActivityListByType
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create activities with MEMO_COMMENT type
_
,
err
=
ts
.
CreateActivity
(
ctx
,
&
store
.
Activity
{
CreatorID
:
user
.
ID
,
Type
:
store
.
ActivityTypeMemoComment
,
Level
:
store
.
ActivityLevelInfo
,
Payload
:
&
storepb
.
ActivityPayload
{},
})
require
.
NoError
(
t
,
err
)
_
,
err
=
ts
.
CreateActivity
(
ctx
,
&
store
.
Activity
{
CreatorID
:
user
.
ID
,
Type
:
store
.
ActivityTypeMemoComment
,
Level
:
store
.
ActivityLevelInfo
,
Payload
:
&
storepb
.
ActivityPayload
{},
})
require
.
NoError
(
t
,
err
)
// List by type
activityType
:=
store
.
ActivityTypeMemoComment
activities
,
err
:=
ts
.
ListActivities
(
ctx
,
&
store
.
FindActivity
{
Type
:
&
activityType
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
activities
,
2
)
for
_
,
activity
:=
range
activities
{
require
.
Equal
(
t
,
store
.
ActivityTypeMemoComment
,
activity
.
Type
)
}
ts
.
Close
()
}
func
TestActivityPayloadMemoComment
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create activity with MemoComment payload
memoID
:=
int32
(
123
)
relatedMemoID
:=
int32
(
456
)
activity
,
err
:=
ts
.
CreateActivity
(
ctx
,
&
store
.
Activity
{
CreatorID
:
user
.
ID
,
Type
:
store
.
ActivityTypeMemoComment
,
Level
:
store
.
ActivityLevelInfo
,
Payload
:
&
storepb
.
ActivityPayload
{
MemoComment
:
&
storepb
.
ActivityMemoCommentPayload
{
MemoId
:
memoID
,
RelatedMemoId
:
relatedMemoID
,
},
},
})
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
activity
.
Payload
)
require
.
NotNil
(
t
,
activity
.
Payload
.
MemoComment
)
require
.
Equal
(
t
,
memoID
,
activity
.
Payload
.
MemoComment
.
MemoId
)
require
.
Equal
(
t
,
relatedMemoID
,
activity
.
Payload
.
MemoComment
.
RelatedMemoId
)
// Verify payload is preserved when listing
found
,
err
:=
ts
.
GetActivity
(
ctx
,
&
store
.
FindActivity
{
ID
:
&
activity
.
ID
})
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
found
.
Payload
.
MemoComment
)
require
.
Equal
(
t
,
memoID
,
found
.
Payload
.
MemoComment
.
MemoId
)
require
.
Equal
(
t
,
relatedMemoID
,
found
.
Payload
.
MemoComment
.
RelatedMemoId
)
ts
.
Close
()
}
func
TestActivityEmptyPayload
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create activity with empty payload
activity
,
err
:=
ts
.
CreateActivity
(
ctx
,
&
store
.
Activity
{
CreatorID
:
user
.
ID
,
Type
:
store
.
ActivityTypeMemoComment
,
Level
:
store
.
ActivityLevelInfo
,
Payload
:
&
storepb
.
ActivityPayload
{},
})
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
activity
.
Payload
)
// Verify empty payload is handled correctly
found
,
err
:=
ts
.
GetActivity
(
ctx
,
&
store
.
FindActivity
{
ID
:
&
activity
.
ID
})
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
found
.
Payload
)
require
.
Nil
(
t
,
found
.
Payload
.
MemoComment
)
ts
.
Close
()
}
func
TestActivityLevel
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create activity with INFO level
activity
,
err
:=
ts
.
CreateActivity
(
ctx
,
&
store
.
Activity
{
CreatorID
:
user
.
ID
,
Type
:
store
.
ActivityTypeMemoComment
,
Level
:
store
.
ActivityLevelInfo
,
Payload
:
&
storepb
.
ActivityPayload
{},
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
store
.
ActivityLevelInfo
,
activity
.
Level
)
// Verify level is preserved when listing
found
,
err
:=
ts
.
GetActivity
(
ctx
,
&
store
.
FindActivity
{
ID
:
&
activity
.
ID
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
store
.
ActivityLevelInfo
,
found
.
Level
)
ts
.
Close
()
}
func
TestActivityCreatorID
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user1
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
user2
,
err
:=
createTestingUserWithRole
(
ctx
,
ts
,
"user2"
,
store
.
RoleUser
)
require
.
NoError
(
t
,
err
)
// Create activity for user1
activity1
,
err
:=
ts
.
CreateActivity
(
ctx
,
&
store
.
Activity
{
CreatorID
:
user1
.
ID
,
Type
:
store
.
ActivityTypeMemoComment
,
Level
:
store
.
ActivityLevelInfo
,
Payload
:
&
storepb
.
ActivityPayload
{},
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
user1
.
ID
,
activity1
.
CreatorID
)
// Create activity for user2
activity2
,
err
:=
ts
.
CreateActivity
(
ctx
,
&
store
.
Activity
{
CreatorID
:
user2
.
ID
,
Type
:
store
.
ActivityTypeMemoComment
,
Level
:
store
.
ActivityLevelInfo
,
Payload
:
&
storepb
.
ActivityPayload
{},
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
user2
.
ID
,
activity2
.
CreatorID
)
// List all and verify creator IDs
activities
,
err
:=
ts
.
ListActivities
(
ctx
,
&
store
.
FindActivity
{})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
activities
,
2
)
ts
.
Close
()
}
func
TestActivityCreatedTs
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
activity
,
err
:=
ts
.
CreateActivity
(
ctx
,
&
store
.
Activity
{
CreatorID
:
user
.
ID
,
Type
:
store
.
ActivityTypeMemoComment
,
Level
:
store
.
ActivityLevelInfo
,
Payload
:
&
storepb
.
ActivityPayload
{},
})
require
.
NoError
(
t
,
err
)
require
.
NotZero
(
t
,
activity
.
CreatedTs
)
// Verify timestamp is preserved when listing
found
,
err
:=
ts
.
GetActivity
(
ctx
,
&
store
.
FindActivity
{
ID
:
&
activity
.
ID
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
activity
.
CreatedTs
,
found
.
CreatedTs
)
ts
.
Close
()
}
func
TestActivityListEmpty
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
// List activities when none exist
activities
,
err
:=
ts
.
ListActivities
(
ctx
,
&
store
.
FindActivity
{})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
activities
,
0
)
ts
.
Close
()
}
func
TestActivityListWithIDAndType
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
activity
,
err
:=
ts
.
CreateActivity
(
ctx
,
&
store
.
Activity
{
CreatorID
:
user
.
ID
,
Type
:
store
.
ActivityTypeMemoComment
,
Level
:
store
.
ActivityLevelInfo
,
Payload
:
&
storepb
.
ActivityPayload
{},
})
require
.
NoError
(
t
,
err
)
// List with both ID and Type filters
activityType
:=
store
.
ActivityTypeMemoComment
activities
,
err
:=
ts
.
ListActivities
(
ctx
,
&
store
.
FindActivity
{
ID
:
&
activity
.
ID
,
Type
:
&
activityType
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
activities
,
1
)
require
.
Equal
(
t
,
activity
.
ID
,
activities
[
0
]
.
ID
)
ts
.
Close
()
}
func
TestActivityPayloadComplexMemoComment
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create a memo first to use its ID
memo
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"test-memo-for-activity"
,
CreatorID
:
user
.
ID
,
Content
:
"Test memo content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
// Create comment memo
commentMemo
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"comment-memo"
,
CreatorID
:
user
.
ID
,
Content
:
"This is a comment"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
// Create activity with real memo IDs
activity
,
err
:=
ts
.
CreateActivity
(
ctx
,
&
store
.
Activity
{
CreatorID
:
user
.
ID
,
Type
:
store
.
ActivityTypeMemoComment
,
Level
:
store
.
ActivityLevelInfo
,
Payload
:
&
storepb
.
ActivityPayload
{
MemoComment
:
&
storepb
.
ActivityMemoCommentPayload
{
MemoId
:
memo
.
ID
,
RelatedMemoId
:
commentMemo
.
ID
,
},
},
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
memo
.
ID
,
activity
.
Payload
.
MemoComment
.
MemoId
)
require
.
Equal
(
t
,
commentMemo
.
ID
,
activity
.
Payload
.
MemoComment
.
RelatedMemoId
)
// Verify payload is preserved
found
,
err
:=
ts
.
GetActivity
(
ctx
,
&
store
.
FindActivity
{
ID
:
&
activity
.
ID
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
memo
.
ID
,
found
.
Payload
.
MemoComment
.
MemoId
)
require
.
Equal
(
t
,
commentMemo
.
ID
,
found
.
Payload
.
MemoComment
.
RelatedMemoId
)
ts
.
Close
()
}
store/test/idp_test.go
View file @
31f634b7
...
...
@@ -58,3 +58,384 @@ func TestIdentityProviderStore(t *testing.T) {
require
.
Equal
(
t
,
0
,
len
(
idpList
))
ts
.
Close
()
}
func
TestIdentityProviderGetByID
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
// Create IDP
idp
,
err
:=
ts
.
CreateIdentityProvider
(
ctx
,
createTestOAuth2IDP
(
"Test IDP"
))
require
.
NoError
(
t
,
err
)
// Get by ID
found
,
err
:=
ts
.
GetIdentityProvider
(
ctx
,
&
store
.
FindIdentityProvider
{
ID
:
&
idp
.
Id
})
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
found
)
require
.
Equal
(
t
,
idp
.
Id
,
found
.
Id
)
require
.
Equal
(
t
,
idp
.
Name
,
found
.
Name
)
// Get by non-existent ID
nonExistentID
:=
int32
(
99999
)
notFound
,
err
:=
ts
.
GetIdentityProvider
(
ctx
,
&
store
.
FindIdentityProvider
{
ID
:
&
nonExistentID
})
require
.
NoError
(
t
,
err
)
require
.
Nil
(
t
,
notFound
)
ts
.
Close
()
}
func
TestIdentityProviderListMultiple
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
// Create multiple IDPs
_
,
err
:=
ts
.
CreateIdentityProvider
(
ctx
,
createTestOAuth2IDP
(
"GitHub OAuth"
))
require
.
NoError
(
t
,
err
)
_
,
err
=
ts
.
CreateIdentityProvider
(
ctx
,
createTestOAuth2IDP
(
"Google OAuth"
))
require
.
NoError
(
t
,
err
)
_
,
err
=
ts
.
CreateIdentityProvider
(
ctx
,
createTestOAuth2IDP
(
"GitLab OAuth"
))
require
.
NoError
(
t
,
err
)
// List all
idpList
,
err
:=
ts
.
ListIdentityProviders
(
ctx
,
&
store
.
FindIdentityProvider
{})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
idpList
,
3
)
ts
.
Close
()
}
func
TestIdentityProviderListByID
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
// Create multiple IDPs
idp1
,
err
:=
ts
.
CreateIdentityProvider
(
ctx
,
createTestOAuth2IDP
(
"GitHub OAuth"
))
require
.
NoError
(
t
,
err
)
_
,
err
=
ts
.
CreateIdentityProvider
(
ctx
,
createTestOAuth2IDP
(
"Google OAuth"
))
require
.
NoError
(
t
,
err
)
// List by specific ID
idpList
,
err
:=
ts
.
ListIdentityProviders
(
ctx
,
&
store
.
FindIdentityProvider
{
ID
:
&
idp1
.
Id
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
idpList
,
1
)
require
.
Equal
(
t
,
"GitHub OAuth"
,
idpList
[
0
]
.
Name
)
ts
.
Close
()
}
func
TestIdentityProviderUpdateName
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
idp
,
err
:=
ts
.
CreateIdentityProvider
(
ctx
,
createTestOAuth2IDP
(
"Original Name"
))
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
"Original Name"
,
idp
.
Name
)
// Update name
newName
:=
"Updated Name"
updated
,
err
:=
ts
.
UpdateIdentityProvider
(
ctx
,
&
store
.
UpdateIdentityProviderV1
{
ID
:
idp
.
Id
,
Type
:
storepb
.
IdentityProvider_OAUTH2
,
Name
:
&
newName
,
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
"Updated Name"
,
updated
.
Name
)
// Verify update persisted
found
,
err
:=
ts
.
GetIdentityProvider
(
ctx
,
&
store
.
FindIdentityProvider
{
ID
:
&
idp
.
Id
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
"Updated Name"
,
found
.
Name
)
ts
.
Close
()
}
func
TestIdentityProviderUpdateIdentifierFilter
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
idp
,
err
:=
ts
.
CreateIdentityProvider
(
ctx
,
createTestOAuth2IDP
(
"Test IDP"
))
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
""
,
idp
.
IdentifierFilter
)
// Update identifier filter
newFilter
:=
"@example.com$"
updated
,
err
:=
ts
.
UpdateIdentityProvider
(
ctx
,
&
store
.
UpdateIdentityProviderV1
{
ID
:
idp
.
Id
,
Type
:
storepb
.
IdentityProvider_OAUTH2
,
IdentifierFilter
:
&
newFilter
,
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
"@example.com$"
,
updated
.
IdentifierFilter
)
// Verify update persisted
found
,
err
:=
ts
.
GetIdentityProvider
(
ctx
,
&
store
.
FindIdentityProvider
{
ID
:
&
idp
.
Id
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
"@example.com$"
,
found
.
IdentifierFilter
)
ts
.
Close
()
}
func
TestIdentityProviderUpdateConfig
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
idp
,
err
:=
ts
.
CreateIdentityProvider
(
ctx
,
createTestOAuth2IDP
(
"Test IDP"
))
require
.
NoError
(
t
,
err
)
// Update config
newConfig
:=
&
storepb
.
IdentityProviderConfig
{
Config
:
&
storepb
.
IdentityProviderConfig_Oauth2Config
{
Oauth2Config
:
&
storepb
.
OAuth2Config
{
ClientId
:
"new_client_id"
,
ClientSecret
:
"new_client_secret"
,
AuthUrl
:
"https://newprovider.com/auth"
,
TokenUrl
:
"https://newprovider.com/token"
,
UserInfoUrl
:
"https://newprovider.com/user"
,
Scopes
:
[]
string
{
"openid"
,
"profile"
,
"email"
},
FieldMapping
:
&
storepb
.
FieldMapping
{
Identifier
:
"sub"
,
DisplayName
:
"name"
,
Email
:
"email"
,
},
},
},
}
updated
,
err
:=
ts
.
UpdateIdentityProvider
(
ctx
,
&
store
.
UpdateIdentityProviderV1
{
ID
:
idp
.
Id
,
Type
:
storepb
.
IdentityProvider_OAUTH2
,
Config
:
newConfig
,
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
"new_client_id"
,
updated
.
Config
.
GetOauth2Config
()
.
ClientId
)
require
.
Equal
(
t
,
"new_client_secret"
,
updated
.
Config
.
GetOauth2Config
()
.
ClientSecret
)
require
.
Contains
(
t
,
updated
.
Config
.
GetOauth2Config
()
.
Scopes
,
"openid"
)
// Verify update persisted
found
,
err
:=
ts
.
GetIdentityProvider
(
ctx
,
&
store
.
FindIdentityProvider
{
ID
:
&
idp
.
Id
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
"new_client_id"
,
found
.
Config
.
GetOauth2Config
()
.
ClientId
)
ts
.
Close
()
}
func
TestIdentityProviderUpdateMultipleFields
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
idp
,
err
:=
ts
.
CreateIdentityProvider
(
ctx
,
createTestOAuth2IDP
(
"Original"
))
require
.
NoError
(
t
,
err
)
// Update multiple fields at once
newName
:=
"Updated IDP"
newFilter
:=
"^admin@"
updated
,
err
:=
ts
.
UpdateIdentityProvider
(
ctx
,
&
store
.
UpdateIdentityProviderV1
{
ID
:
idp
.
Id
,
Type
:
storepb
.
IdentityProvider_OAUTH2
,
Name
:
&
newName
,
IdentifierFilter
:
&
newFilter
,
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
"Updated IDP"
,
updated
.
Name
)
require
.
Equal
(
t
,
"^admin@"
,
updated
.
IdentifierFilter
)
ts
.
Close
()
}
func
TestIdentityProviderDelete
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
idp
,
err
:=
ts
.
CreateIdentityProvider
(
ctx
,
createTestOAuth2IDP
(
"Test IDP"
))
require
.
NoError
(
t
,
err
)
// Delete
err
=
ts
.
DeleteIdentityProvider
(
ctx
,
&
store
.
DeleteIdentityProvider
{
ID
:
idp
.
Id
})
require
.
NoError
(
t
,
err
)
// Verify deletion
found
,
err
:=
ts
.
GetIdentityProvider
(
ctx
,
&
store
.
FindIdentityProvider
{
ID
:
&
idp
.
Id
})
require
.
NoError
(
t
,
err
)
require
.
Nil
(
t
,
found
)
ts
.
Close
()
}
func
TestIdentityProviderDeleteNotAffectOthers
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
// Create multiple IDPs
idp1
,
err
:=
ts
.
CreateIdentityProvider
(
ctx
,
createTestOAuth2IDP
(
"IDP 1"
))
require
.
NoError
(
t
,
err
)
idp2
,
err
:=
ts
.
CreateIdentityProvider
(
ctx
,
createTestOAuth2IDP
(
"IDP 2"
))
require
.
NoError
(
t
,
err
)
// Delete first one
err
=
ts
.
DeleteIdentityProvider
(
ctx
,
&
store
.
DeleteIdentityProvider
{
ID
:
idp1
.
Id
})
require
.
NoError
(
t
,
err
)
// Verify second still exists
found
,
err
:=
ts
.
GetIdentityProvider
(
ctx
,
&
store
.
FindIdentityProvider
{
ID
:
&
idp2
.
Id
})
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
found
)
require
.
Equal
(
t
,
"IDP 2"
,
found
.
Name
)
// Verify list only contains second
idpList
,
err
:=
ts
.
ListIdentityProviders
(
ctx
,
&
store
.
FindIdentityProvider
{})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
idpList
,
1
)
ts
.
Close
()
}
func
TestIdentityProviderOAuth2ConfigScopes
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
// Create IDP with multiple scopes
idp
,
err
:=
ts
.
CreateIdentityProvider
(
ctx
,
&
storepb
.
IdentityProvider
{
Name
:
"Multi-Scope OAuth"
,
Type
:
storepb
.
IdentityProvider_OAUTH2
,
Config
:
&
storepb
.
IdentityProviderConfig
{
Config
:
&
storepb
.
IdentityProviderConfig_Oauth2Config
{
Oauth2Config
:
&
storepb
.
OAuth2Config
{
ClientId
:
"client_id"
,
ClientSecret
:
"client_secret"
,
AuthUrl
:
"https://provider.com/auth"
,
TokenUrl
:
"https://provider.com/token"
,
UserInfoUrl
:
"https://provider.com/userinfo"
,
Scopes
:
[]
string
{
"openid"
,
"profile"
,
"email"
,
"groups"
},
FieldMapping
:
&
storepb
.
FieldMapping
{
Identifier
:
"sub"
,
DisplayName
:
"name"
,
Email
:
"email"
,
},
},
},
},
})
require
.
NoError
(
t
,
err
)
// Verify scopes are preserved
found
,
err
:=
ts
.
GetIdentityProvider
(
ctx
,
&
store
.
FindIdentityProvider
{
ID
:
&
idp
.
Id
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
found
.
Config
.
GetOauth2Config
()
.
Scopes
,
4
)
require
.
Contains
(
t
,
found
.
Config
.
GetOauth2Config
()
.
Scopes
,
"openid"
)
require
.
Contains
(
t
,
found
.
Config
.
GetOauth2Config
()
.
Scopes
,
"groups"
)
ts
.
Close
()
}
func
TestIdentityProviderFieldMapping
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
// Create IDP with custom field mapping
idp
,
err
:=
ts
.
CreateIdentityProvider
(
ctx
,
&
storepb
.
IdentityProvider
{
Name
:
"Custom Field Mapping"
,
Type
:
storepb
.
IdentityProvider_OAUTH2
,
Config
:
&
storepb
.
IdentityProviderConfig
{
Config
:
&
storepb
.
IdentityProviderConfig_Oauth2Config
{
Oauth2Config
:
&
storepb
.
OAuth2Config
{
ClientId
:
"client_id"
,
ClientSecret
:
"client_secret"
,
AuthUrl
:
"https://provider.com/auth"
,
TokenUrl
:
"https://provider.com/token"
,
UserInfoUrl
:
"https://provider.com/userinfo"
,
Scopes
:
[]
string
{
"login"
},
FieldMapping
:
&
storepb
.
FieldMapping
{
Identifier
:
"preferred_username"
,
DisplayName
:
"full_name"
,
Email
:
"email_address"
,
},
},
},
},
})
require
.
NoError
(
t
,
err
)
// Verify field mapping is preserved
found
,
err
:=
ts
.
GetIdentityProvider
(
ctx
,
&
store
.
FindIdentityProvider
{
ID
:
&
idp
.
Id
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
"preferred_username"
,
found
.
Config
.
GetOauth2Config
()
.
FieldMapping
.
Identifier
)
require
.
Equal
(
t
,
"full_name"
,
found
.
Config
.
GetOauth2Config
()
.
FieldMapping
.
DisplayName
)
require
.
Equal
(
t
,
"email_address"
,
found
.
Config
.
GetOauth2Config
()
.
FieldMapping
.
Email
)
ts
.
Close
()
}
func
TestIdentityProviderIdentifierFilterPatterns
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
testCases
:=
[]
struct
{
name
string
filter
string
}{
{
"Domain filter"
,
"@company
\\
.com$"
},
{
"Prefix filter"
,
"^admin_"
},
{
"Complex regex"
,
"^[a-z]+@(dept1|dept2)
\\
.example
\\
.com$"
},
{
"Empty filter"
,
""
},
}
for
_
,
tc
:=
range
testCases
{
t
.
Run
(
tc
.
name
,
func
(
t
*
testing
.
T
)
{
idp
,
err
:=
ts
.
CreateIdentityProvider
(
ctx
,
&
storepb
.
IdentityProvider
{
Name
:
tc
.
name
,
Type
:
storepb
.
IdentityProvider_OAUTH2
,
IdentifierFilter
:
tc
.
filter
,
Config
:
&
storepb
.
IdentityProviderConfig
{
Config
:
&
storepb
.
IdentityProviderConfig_Oauth2Config
{
Oauth2Config
:
&
storepb
.
OAuth2Config
{
ClientId
:
"client_id"
,
ClientSecret
:
"client_secret"
,
AuthUrl
:
"https://provider.com/auth"
,
TokenUrl
:
"https://provider.com/token"
,
UserInfoUrl
:
"https://provider.com/userinfo"
,
Scopes
:
[]
string
{
"login"
},
FieldMapping
:
&
storepb
.
FieldMapping
{
Identifier
:
"sub"
,
},
},
},
},
})
require
.
NoError
(
t
,
err
)
found
,
err
:=
ts
.
GetIdentityProvider
(
ctx
,
&
store
.
FindIdentityProvider
{
ID
:
&
idp
.
Id
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
tc
.
filter
,
found
.
IdentifierFilter
)
// Cleanup
err
=
ts
.
DeleteIdentityProvider
(
ctx
,
&
store
.
DeleteIdentityProvider
{
ID
:
idp
.
Id
})
require
.
NoError
(
t
,
err
)
})
}
ts
.
Close
()
}
// Helper function to create a test OAuth2 IDP
func
createTestOAuth2IDP
(
name
string
)
*
storepb
.
IdentityProvider
{
return
&
storepb
.
IdentityProvider
{
Name
:
name
,
Type
:
storepb
.
IdentityProvider_OAUTH2
,
IdentifierFilter
:
""
,
Config
:
&
storepb
.
IdentityProviderConfig
{
Config
:
&
storepb
.
IdentityProviderConfig_Oauth2Config
{
Oauth2Config
:
&
storepb
.
OAuth2Config
{
ClientId
:
"client_id"
,
ClientSecret
:
"client_secret"
,
AuthUrl
:
"https://provider.com/auth"
,
TokenUrl
:
"https://provider.com/token"
,
UserInfoUrl
:
"https://provider.com/userinfo"
,
Scopes
:
[]
string
{
"login"
},
FieldMapping
:
&
storepb
.
FieldMapping
{
Identifier
:
"login"
,
DisplayName
:
"name"
,
Email
:
"email"
,
},
},
},
},
}
}
store/test/inbox_test.go
View file @
31f634b7
...
...
@@ -52,3 +52,528 @@ func TestInboxStore(t *testing.T) {
require
.
Equal
(
t
,
0
,
len
(
inboxes
))
ts
.
Close
()
}
func
TestInboxListByID
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
inbox
,
err
:=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
// List by ID
inboxes
,
err
:=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ID
:
&
inbox
.
ID
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
1
)
require
.
Equal
(
t
,
inbox
.
ID
,
inboxes
[
0
]
.
ID
)
// List by non-existent ID
nonExistentID
:=
int32
(
99999
)
inboxes
,
err
=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ID
:
&
nonExistentID
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
0
)
ts
.
Close
()
}
func
TestInboxListBySenderID
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user1
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
user2
,
err
:=
createTestingUserWithRole
(
ctx
,
ts
,
"user2"
,
store
.
RoleUser
)
require
.
NoError
(
t
,
err
)
// Create inbox from system bot (senderID = 0)
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user1
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
// Create inbox from user2
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
user2
.
ID
,
ReceiverID
:
user1
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
// List by sender ID = user2
inboxes
,
err
:=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
SenderID
:
&
user2
.
ID
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
1
)
require
.
Equal
(
t
,
user2
.
ID
,
inboxes
[
0
]
.
SenderID
)
// List by sender ID = 0 (system bot)
systemBotID
:=
int32
(
0
)
inboxes
,
err
=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
SenderID
:
&
systemBotID
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
1
)
require
.
Equal
(
t
,
int32
(
0
),
inboxes
[
0
]
.
SenderID
)
ts
.
Close
()
}
func
TestInboxListByStatus
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create UNREAD inbox
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
// Create another inbox and archive it
inbox2
,
err
:=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
_
,
err
=
ts
.
UpdateInbox
(
ctx
,
&
store
.
UpdateInbox
{
ID
:
inbox2
.
ID
,
Status
:
store
.
ARCHIVED
})
require
.
NoError
(
t
,
err
)
// List by UNREAD status
unreadStatus
:=
store
.
UNREAD
inboxes
,
err
:=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
Status
:
&
unreadStatus
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
1
)
require
.
Equal
(
t
,
store
.
UNREAD
,
inboxes
[
0
]
.
Status
)
// List by ARCHIVED status
archivedStatus
:=
store
.
ARCHIVED
inboxes
,
err
=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
Status
:
&
archivedStatus
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
1
)
require
.
Equal
(
t
,
store
.
ARCHIVED
,
inboxes
[
0
]
.
Status
)
ts
.
Close
()
}
func
TestInboxListByMessageType
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create MEMO_COMMENT inboxes
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
// List by MEMO_COMMENT type
memoCommentType
:=
storepb
.
InboxMessage_MEMO_COMMENT
inboxes
,
err
:=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
MessageType
:
&
memoCommentType
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
2
)
for
_
,
inbox
:=
range
inboxes
{
require
.
Equal
(
t
,
storepb
.
InboxMessage_MEMO_COMMENT
,
inbox
.
Message
.
Type
)
}
ts
.
Close
()
}
func
TestInboxListPagination
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create 5 inboxes
for
i
:=
0
;
i
<
5
;
i
++
{
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
}
// Test Limit only
limit
:=
3
inboxes
,
err
:=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user
.
ID
,
Limit
:
&
limit
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
3
)
// Test Limit + Offset (offset requires limit in the implementation)
limit
=
2
offset
:=
2
inboxes
,
err
=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user
.
ID
,
Limit
:
&
limit
,
Offset
:
&
offset
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
2
)
// Test Limit + Offset skipping to end
limit
=
10
offset
=
3
inboxes
,
err
=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user
.
ID
,
Limit
:
&
limit
,
Offset
:
&
offset
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
2
)
// Only 2 remaining after offset of 3
ts
.
Close
()
}
func
TestInboxListCombinedFilters
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user1
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
user2
,
err
:=
createTestingUserWithRole
(
ctx
,
ts
,
"user2"
,
store
.
RoleUser
)
require
.
NoError
(
t
,
err
)
// Create various inboxes
// user2 -> user1, MEMO_COMMENT, UNREAD
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
user2
.
ID
,
ReceiverID
:
user1
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
// user2 -> user1, TYPE_UNSPECIFIED, UNREAD
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
user2
.
ID
,
ReceiverID
:
user1
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_TYPE_UNSPECIFIED
},
})
require
.
NoError
(
t
,
err
)
// system -> user1, MEMO_COMMENT, ARCHIVED
inbox3
,
err
:=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user1
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
_
,
err
=
ts
.
UpdateInbox
(
ctx
,
&
store
.
UpdateInbox
{
ID
:
inbox3
.
ID
,
Status
:
store
.
ARCHIVED
})
require
.
NoError
(
t
,
err
)
// Combined filter: ReceiverID + SenderID + Status
unreadStatus
:=
store
.
UNREAD
inboxes
,
err
:=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user1
.
ID
,
SenderID
:
&
user2
.
ID
,
Status
:
&
unreadStatus
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
2
)
// Combined filter: ReceiverID + MessageType + Status
memoCommentType
:=
storepb
.
InboxMessage_MEMO_COMMENT
inboxes
,
err
=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user1
.
ID
,
MessageType
:
&
memoCommentType
,
Status
:
&
unreadStatus
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
1
)
require
.
Equal
(
t
,
user2
.
ID
,
inboxes
[
0
]
.
SenderID
)
ts
.
Close
()
}
func
TestInboxMessagePayload
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create inbox with message payload containing activity ID
activityID
:=
int32
(
123
)
inbox
,
err
:=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
,
ActivityId
:
&
activityID
,
},
})
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
inbox
.
Message
)
require
.
Equal
(
t
,
storepb
.
InboxMessage_MEMO_COMMENT
,
inbox
.
Message
.
Type
)
require
.
Equal
(
t
,
activityID
,
*
inbox
.
Message
.
ActivityId
)
// List and verify payload is preserved
inboxes
,
err
:=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user
.
ID
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
1
)
require
.
Equal
(
t
,
activityID
,
*
inboxes
[
0
]
.
Message
.
ActivityId
)
ts
.
Close
()
}
func
TestInboxUpdateStatus
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
inbox
,
err
:=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
store
.
UNREAD
,
inbox
.
Status
)
// Update to ARCHIVED
updated
,
err
:=
ts
.
UpdateInbox
(
ctx
,
&
store
.
UpdateInbox
{
ID
:
inbox
.
ID
,
Status
:
store
.
ARCHIVED
,
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
store
.
ARCHIVED
,
updated
.
Status
)
require
.
Equal
(
t
,
inbox
.
ID
,
updated
.
ID
)
// Verify the update persisted
inboxes
,
err
:=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ID
:
&
inbox
.
ID
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
1
)
require
.
Equal
(
t
,
store
.
ARCHIVED
,
inboxes
[
0
]
.
Status
)
ts
.
Close
()
}
func
TestInboxListByMessageTypeMultipleTypes
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create inboxes with different message types
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_TYPE_UNSPECIFIED
},
})
require
.
NoError
(
t
,
err
)
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
// Filter by MEMO_COMMENT - should get 2
memoCommentType
:=
storepb
.
InboxMessage_MEMO_COMMENT
inboxes
,
err
:=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user
.
ID
,
MessageType
:
&
memoCommentType
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
2
)
for
_
,
inbox
:=
range
inboxes
{
require
.
Equal
(
t
,
storepb
.
InboxMessage_MEMO_COMMENT
,
inbox
.
Message
.
Type
)
}
// Filter by TYPE_UNSPECIFIED - should get 1
unspecifiedType
:=
storepb
.
InboxMessage_TYPE_UNSPECIFIED
inboxes
,
err
=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user
.
ID
,
MessageType
:
&
unspecifiedType
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
1
)
require
.
Equal
(
t
,
storepb
.
InboxMessage_TYPE_UNSPECIFIED
,
inboxes
[
0
]
.
Message
.
Type
)
ts
.
Close
()
}
func
TestInboxMessageTypeFilterWithPayload
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create inbox with full payload
activityID
:=
int32
(
456
)
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
,
ActivityId
:
&
activityID
,
},
})
require
.
NoError
(
t
,
err
)
// Create inbox with different type but also has payload
otherActivityID
:=
int32
(
789
)
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_TYPE_UNSPECIFIED
,
ActivityId
:
&
otherActivityID
,
},
})
require
.
NoError
(
t
,
err
)
// Filter by type should work correctly even with complex JSON payload
memoCommentType
:=
storepb
.
InboxMessage_MEMO_COMMENT
inboxes
,
err
:=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user
.
ID
,
MessageType
:
&
memoCommentType
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
1
)
require
.
Equal
(
t
,
activityID
,
*
inboxes
[
0
]
.
Message
.
ActivityId
)
ts
.
Close
()
}
func
TestInboxMessageTypeFilterWithStatusAndPagination
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create multiple inboxes with various combinations
for
i
:=
0
;
i
<
5
;
i
++
{
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
}
// Archive 2 of them
allInboxes
,
err
:=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user
.
ID
})
require
.
NoError
(
t
,
err
)
for
i
:=
0
;
i
<
2
;
i
++
{
_
,
err
=
ts
.
UpdateInbox
(
ctx
,
&
store
.
UpdateInbox
{
ID
:
allInboxes
[
i
]
.
ID
,
Status
:
store
.
ARCHIVED
})
require
.
NoError
(
t
,
err
)
}
// Filter by type + status + pagination
memoCommentType
:=
storepb
.
InboxMessage_MEMO_COMMENT
unreadStatus
:=
store
.
UNREAD
limit
:=
2
inboxes
,
err
:=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user
.
ID
,
MessageType
:
&
memoCommentType
,
Status
:
&
unreadStatus
,
Limit
:
&
limit
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
2
)
for
_
,
inbox
:=
range
inboxes
{
require
.
Equal
(
t
,
storepb
.
InboxMessage_MEMO_COMMENT
,
inbox
.
Message
.
Type
)
require
.
Equal
(
t
,
store
.
UNREAD
,
inbox
.
Status
)
}
// Get next page
offset
:=
2
inboxes
,
err
=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user
.
ID
,
MessageType
:
&
memoCommentType
,
Status
:
&
unreadStatus
,
Limit
:
&
limit
,
Offset
:
&
offset
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
1
)
// Only 1 remaining (3 unread total, got 2, now 1 left)
ts
.
Close
()
}
func
TestInboxMultipleReceivers
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user1
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
user2
,
err
:=
createTestingUserWithRole
(
ctx
,
ts
,
"user2"
,
store
.
RoleUser
)
require
.
NoError
(
t
,
err
)
// Create inbox for user1
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user1
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
// Create inbox for user2
_
,
err
=
ts
.
CreateInbox
(
ctx
,
&
store
.
Inbox
{
SenderID
:
0
,
ReceiverID
:
user2
.
ID
,
Status
:
store
.
UNREAD
,
Message
:
&
storepb
.
InboxMessage
{
Type
:
storepb
.
InboxMessage_MEMO_COMMENT
},
})
require
.
NoError
(
t
,
err
)
// User1 should only see their inbox
inboxes
,
err
:=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user1
.
ID
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
1
)
require
.
Equal
(
t
,
user1
.
ID
,
inboxes
[
0
]
.
ReceiverID
)
// User2 should only see their inbox
inboxes
,
err
=
ts
.
ListInboxes
(
ctx
,
&
store
.
FindInbox
{
ReceiverID
:
&
user2
.
ID
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
inboxes
,
1
)
require
.
Equal
(
t
,
user2
.
ID
,
inboxes
[
0
]
.
ReceiverID
)
ts
.
Close
()
}
store/test/memo_relation_test.go
View file @
31f634b7
...
...
@@ -239,3 +239,432 @@ func TestMemoRelationDifferentTypes(t *testing.T) {
ts
.
Close
()
}
func
TestMemoRelationUpsertSameRelation
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
mainMemo
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"main-memo"
,
CreatorID
:
user
.
ID
,
Content
:
"main memo content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
relatedMemo
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"related-memo"
,
CreatorID
:
user
.
ID
,
Content
:
"related memo content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
// Create relation
_
,
err
=
ts
.
UpsertMemoRelation
(
ctx
,
&
store
.
MemoRelation
{
MemoID
:
mainMemo
.
ID
,
RelatedMemoID
:
relatedMemo
.
ID
,
Type
:
store
.
MemoRelationReference
,
})
require
.
NoError
(
t
,
err
)
// Upsert the same relation again (should not create duplicate)
_
,
err
=
ts
.
UpsertMemoRelation
(
ctx
,
&
store
.
MemoRelation
{
MemoID
:
mainMemo
.
ID
,
RelatedMemoID
:
relatedMemo
.
ID
,
Type
:
store
.
MemoRelationReference
,
})
require
.
NoError
(
t
,
err
)
// Verify only one relation exists
relations
,
err
:=
ts
.
ListMemoRelations
(
ctx
,
&
store
.
FindMemoRelation
{
MemoID
:
&
mainMemo
.
ID
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
relations
,
1
)
ts
.
Close
()
}
func
TestMemoRelationDeleteByType
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
mainMemo
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"main-memo"
,
CreatorID
:
user
.
ID
,
Content
:
"main memo content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
relatedMemo1
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"related-memo-1"
,
CreatorID
:
user
.
ID
,
Content
:
"related memo 1 content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
relatedMemo2
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"related-memo-2"
,
CreatorID
:
user
.
ID
,
Content
:
"related memo 2 content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
// Create reference relations
_
,
err
=
ts
.
UpsertMemoRelation
(
ctx
,
&
store
.
MemoRelation
{
MemoID
:
mainMemo
.
ID
,
RelatedMemoID
:
relatedMemo1
.
ID
,
Type
:
store
.
MemoRelationReference
,
})
require
.
NoError
(
t
,
err
)
// Create comment relation
_
,
err
=
ts
.
UpsertMemoRelation
(
ctx
,
&
store
.
MemoRelation
{
MemoID
:
mainMemo
.
ID
,
RelatedMemoID
:
relatedMemo2
.
ID
,
Type
:
store
.
MemoRelationComment
,
})
require
.
NoError
(
t
,
err
)
// Delete only reference type relations
refType
:=
store
.
MemoRelationReference
err
=
ts
.
DeleteMemoRelation
(
ctx
,
&
store
.
DeleteMemoRelation
{
MemoID
:
&
mainMemo
.
ID
,
Type
:
&
refType
,
})
require
.
NoError
(
t
,
err
)
// Verify only comment relation remains
relations
,
err
:=
ts
.
ListMemoRelations
(
ctx
,
&
store
.
FindMemoRelation
{
MemoID
:
&
mainMemo
.
ID
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
relations
,
1
)
require
.
Equal
(
t
,
store
.
MemoRelationComment
,
relations
[
0
]
.
Type
)
ts
.
Close
()
}
func
TestMemoRelationDeleteByMemoID
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
memo1
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"memo-1"
,
CreatorID
:
user
.
ID
,
Content
:
"memo 1 content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
memo2
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"memo-2"
,
CreatorID
:
user
.
ID
,
Content
:
"memo 2 content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
relatedMemo
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"related-memo"
,
CreatorID
:
user
.
ID
,
Content
:
"related memo content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
// Create relations for both memos
_
,
err
=
ts
.
UpsertMemoRelation
(
ctx
,
&
store
.
MemoRelation
{
MemoID
:
memo1
.
ID
,
RelatedMemoID
:
relatedMemo
.
ID
,
Type
:
store
.
MemoRelationReference
,
})
require
.
NoError
(
t
,
err
)
_
,
err
=
ts
.
UpsertMemoRelation
(
ctx
,
&
store
.
MemoRelation
{
MemoID
:
memo2
.
ID
,
RelatedMemoID
:
relatedMemo
.
ID
,
Type
:
store
.
MemoRelationReference
,
})
require
.
NoError
(
t
,
err
)
// Delete all relations for memo1
err
=
ts
.
DeleteMemoRelation
(
ctx
,
&
store
.
DeleteMemoRelation
{
MemoID
:
&
memo1
.
ID
,
})
require
.
NoError
(
t
,
err
)
// Verify memo1's relations are gone
relations
,
err
:=
ts
.
ListMemoRelations
(
ctx
,
&
store
.
FindMemoRelation
{
MemoID
:
&
memo1
.
ID
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
relations
,
0
)
// Verify memo2's relations still exist
relations
,
err
=
ts
.
ListMemoRelations
(
ctx
,
&
store
.
FindMemoRelation
{
MemoID
:
&
memo2
.
ID
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
relations
,
1
)
ts
.
Close
()
}
func
TestMemoRelationListByRelatedMemoID
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create a memo that will be referenced by others
targetMemo
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"target-memo"
,
CreatorID
:
user
.
ID
,
Content
:
"target memo content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
// Create memos that reference the target
referrer1
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"referrer-1"
,
CreatorID
:
user
.
ID
,
Content
:
"referrer 1 content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
referrer2
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"referrer-2"
,
CreatorID
:
user
.
ID
,
Content
:
"referrer 2 content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
// Create relations pointing to target
_
,
err
=
ts
.
UpsertMemoRelation
(
ctx
,
&
store
.
MemoRelation
{
MemoID
:
referrer1
.
ID
,
RelatedMemoID
:
targetMemo
.
ID
,
Type
:
store
.
MemoRelationReference
,
})
require
.
NoError
(
t
,
err
)
_
,
err
=
ts
.
UpsertMemoRelation
(
ctx
,
&
store
.
MemoRelation
{
MemoID
:
referrer2
.
ID
,
RelatedMemoID
:
targetMemo
.
ID
,
Type
:
store
.
MemoRelationComment
,
})
require
.
NoError
(
t
,
err
)
// List by related memo ID (find all memos that reference the target)
relations
,
err
:=
ts
.
ListMemoRelations
(
ctx
,
&
store
.
FindMemoRelation
{
RelatedMemoID
:
&
targetMemo
.
ID
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
relations
,
2
)
ts
.
Close
()
}
func
TestMemoRelationListCombinedFilters
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
mainMemo
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"main-memo"
,
CreatorID
:
user
.
ID
,
Content
:
"main memo content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
relatedMemo1
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"related-memo-1"
,
CreatorID
:
user
.
ID
,
Content
:
"related memo 1 content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
relatedMemo2
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"related-memo-2"
,
CreatorID
:
user
.
ID
,
Content
:
"related memo 2 content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
// Create multiple relations
_
,
err
=
ts
.
UpsertMemoRelation
(
ctx
,
&
store
.
MemoRelation
{
MemoID
:
mainMemo
.
ID
,
RelatedMemoID
:
relatedMemo1
.
ID
,
Type
:
store
.
MemoRelationReference
,
})
require
.
NoError
(
t
,
err
)
_
,
err
=
ts
.
UpsertMemoRelation
(
ctx
,
&
store
.
MemoRelation
{
MemoID
:
mainMemo
.
ID
,
RelatedMemoID
:
relatedMemo2
.
ID
,
Type
:
store
.
MemoRelationComment
,
})
require
.
NoError
(
t
,
err
)
// List with MemoID and Type filter
refType
:=
store
.
MemoRelationReference
relations
,
err
:=
ts
.
ListMemoRelations
(
ctx
,
&
store
.
FindMemoRelation
{
MemoID
:
&
mainMemo
.
ID
,
Type
:
&
refType
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
relations
,
1
)
require
.
Equal
(
t
,
relatedMemo1
.
ID
,
relations
[
0
]
.
RelatedMemoID
)
// List with MemoID, RelatedMemoID, and Type filter
commentType
:=
store
.
MemoRelationComment
relations
,
err
=
ts
.
ListMemoRelations
(
ctx
,
&
store
.
FindMemoRelation
{
MemoID
:
&
mainMemo
.
ID
,
RelatedMemoID
:
&
relatedMemo2
.
ID
,
Type
:
&
commentType
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
relations
,
1
)
ts
.
Close
()
}
func
TestMemoRelationListEmpty
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
memo
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"memo-no-relations"
,
CreatorID
:
user
.
ID
,
Content
:
"memo with no relations"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
// List relations for memo with none
relations
,
err
:=
ts
.
ListMemoRelations
(
ctx
,
&
store
.
FindMemoRelation
{
MemoID
:
&
memo
.
ID
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
relations
,
0
)
ts
.
Close
()
}
func
TestMemoRelationBidirectional
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
memoA
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"memo-a"
,
CreatorID
:
user
.
ID
,
Content
:
"memo A content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
memoB
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"memo-b"
,
CreatorID
:
user
.
ID
,
Content
:
"memo B content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
// Create relation A -> B
_
,
err
=
ts
.
UpsertMemoRelation
(
ctx
,
&
store
.
MemoRelation
{
MemoID
:
memoA
.
ID
,
RelatedMemoID
:
memoB
.
ID
,
Type
:
store
.
MemoRelationReference
,
})
require
.
NoError
(
t
,
err
)
// Create relation B -> A (reverse direction)
_
,
err
=
ts
.
UpsertMemoRelation
(
ctx
,
&
store
.
MemoRelation
{
MemoID
:
memoB
.
ID
,
RelatedMemoID
:
memoA
.
ID
,
Type
:
store
.
MemoRelationReference
,
})
require
.
NoError
(
t
,
err
)
// Verify A -> B exists
relationsFromA
,
err
:=
ts
.
ListMemoRelations
(
ctx
,
&
store
.
FindMemoRelation
{
MemoID
:
&
memoA
.
ID
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
relationsFromA
,
1
)
require
.
Equal
(
t
,
memoB
.
ID
,
relationsFromA
[
0
]
.
RelatedMemoID
)
// Verify B -> A exists
relationsFromB
,
err
:=
ts
.
ListMemoRelations
(
ctx
,
&
store
.
FindMemoRelation
{
MemoID
:
&
memoB
.
ID
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
relationsFromB
,
1
)
require
.
Equal
(
t
,
memoA
.
ID
,
relationsFromB
[
0
]
.
RelatedMemoID
)
ts
.
Close
()
}
func
TestMemoRelationMultipleRelationsToSameMemo
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
mainMemo
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"main-memo"
,
CreatorID
:
user
.
ID
,
Content
:
"main memo content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
// Create multiple memos that all relate to the main memo
for
i
:=
1
;
i
<=
5
;
i
++
{
relatedMemo
,
err
:=
ts
.
CreateMemo
(
ctx
,
&
store
.
Memo
{
UID
:
"related-memo-"
+
string
(
rune
(
'0'
+
i
)),
CreatorID
:
user
.
ID
,
Content
:
"related memo content"
,
Visibility
:
store
.
Public
,
})
require
.
NoError
(
t
,
err
)
_
,
err
=
ts
.
UpsertMemoRelation
(
ctx
,
&
store
.
MemoRelation
{
MemoID
:
mainMemo
.
ID
,
RelatedMemoID
:
relatedMemo
.
ID
,
Type
:
store
.
MemoRelationReference
,
})
require
.
NoError
(
t
,
err
)
}
// Verify all 5 relations exist
relations
,
err
:=
ts
.
ListMemoRelations
(
ctx
,
&
store
.
FindMemoRelation
{
MemoID
:
&
mainMemo
.
ID
,
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
relations
,
5
)
ts
.
Close
()
}
store/test/user_setting_test.go
View file @
31f634b7
...
...
@@ -5,6 +5,7 @@ import (
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/timestamppb"
storepb
"github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store"
...
...
@@ -308,3 +309,332 @@ func TestUserSettingShortcuts(t *testing.T) {
ts
.
Close
()
}
func
TestUserSettingGetUserByPATHash
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create a PAT with a known hash
patHash
:=
"test-pat-hash-12345"
pat
:=
&
storepb
.
PersonalAccessTokensUserSetting_PersonalAccessToken
{
TokenId
:
"pat-test-1"
,
TokenHash
:
patHash
,
Description
:
"Test PAT for lookup"
,
}
err
=
ts
.
AddUserPersonalAccessToken
(
ctx
,
user
.
ID
,
pat
)
require
.
NoError
(
t
,
err
)
// Lookup user by PAT hash
result
,
err
:=
ts
.
GetUserByPATHash
(
ctx
,
patHash
)
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
result
)
require
.
Equal
(
t
,
user
.
ID
,
result
.
UserID
)
require
.
NotNil
(
t
,
result
.
User
)
require
.
Equal
(
t
,
user
.
Username
,
result
.
User
.
Username
)
require
.
NotNil
(
t
,
result
.
PAT
)
require
.
Equal
(
t
,
"pat-test-1"
,
result
.
PAT
.
TokenId
)
require
.
Equal
(
t
,
"Test PAT for lookup"
,
result
.
PAT
.
Description
)
ts
.
Close
()
}
func
TestUserSettingGetUserByPATHashNotFound
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
_
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Lookup non-existent PAT hash
result
,
err
:=
ts
.
GetUserByPATHash
(
ctx
,
"non-existent-hash"
)
require
.
Error
(
t
,
err
)
require
.
Nil
(
t
,
result
)
ts
.
Close
()
}
func
TestUserSettingGetUserByPATHashMultipleUsers
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user1
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
user2
,
err
:=
createTestingUserWithRole
(
ctx
,
ts
,
"user2"
,
store
.
RoleUser
)
require
.
NoError
(
t
,
err
)
// Create PATs for both users
pat1Hash
:=
"user1-pat-hash"
err
=
ts
.
AddUserPersonalAccessToken
(
ctx
,
user1
.
ID
,
&
storepb
.
PersonalAccessTokensUserSetting_PersonalAccessToken
{
TokenId
:
"pat-user1"
,
TokenHash
:
pat1Hash
,
Description
:
"User 1 PAT"
,
})
require
.
NoError
(
t
,
err
)
pat2Hash
:=
"user2-pat-hash"
err
=
ts
.
AddUserPersonalAccessToken
(
ctx
,
user2
.
ID
,
&
storepb
.
PersonalAccessTokensUserSetting_PersonalAccessToken
{
TokenId
:
"pat-user2"
,
TokenHash
:
pat2Hash
,
Description
:
"User 2 PAT"
,
})
require
.
NoError
(
t
,
err
)
// Lookup user1's PAT
result1
,
err
:=
ts
.
GetUserByPATHash
(
ctx
,
pat1Hash
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
user1
.
ID
,
result1
.
UserID
)
require
.
Equal
(
t
,
user1
.
Username
,
result1
.
User
.
Username
)
// Lookup user2's PAT
result2
,
err
:=
ts
.
GetUserByPATHash
(
ctx
,
pat2Hash
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
user2
.
ID
,
result2
.
UserID
)
require
.
Equal
(
t
,
user2
.
Username
,
result2
.
User
.
Username
)
ts
.
Close
()
}
func
TestUserSettingGetUserByPATHashMultiplePATsSameUser
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create multiple PATs for the same user
pat1Hash
:=
"first-pat-hash"
err
=
ts
.
AddUserPersonalAccessToken
(
ctx
,
user
.
ID
,
&
storepb
.
PersonalAccessTokensUserSetting_PersonalAccessToken
{
TokenId
:
"pat-1"
,
TokenHash
:
pat1Hash
,
Description
:
"First PAT"
,
})
require
.
NoError
(
t
,
err
)
pat2Hash
:=
"second-pat-hash"
err
=
ts
.
AddUserPersonalAccessToken
(
ctx
,
user
.
ID
,
&
storepb
.
PersonalAccessTokensUserSetting_PersonalAccessToken
{
TokenId
:
"pat-2"
,
TokenHash
:
pat2Hash
,
Description
:
"Second PAT"
,
})
require
.
NoError
(
t
,
err
)
// Both PATs should resolve to the same user
result1
,
err
:=
ts
.
GetUserByPATHash
(
ctx
,
pat1Hash
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
user
.
ID
,
result1
.
UserID
)
require
.
Equal
(
t
,
"pat-1"
,
result1
.
PAT
.
TokenId
)
result2
,
err
:=
ts
.
GetUserByPATHash
(
ctx
,
pat2Hash
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
user
.
ID
,
result2
.
UserID
)
require
.
Equal
(
t
,
"pat-2"
,
result2
.
PAT
.
TokenId
)
ts
.
Close
()
}
func
TestUserSettingUpdatePATLastUsed
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create a PAT
patHash
:=
"pat-hash-for-update"
err
=
ts
.
AddUserPersonalAccessToken
(
ctx
,
user
.
ID
,
&
storepb
.
PersonalAccessTokensUserSetting_PersonalAccessToken
{
TokenId
:
"pat-update-test"
,
TokenHash
:
patHash
,
Description
:
"PAT for update test"
,
})
require
.
NoError
(
t
,
err
)
// Update last used timestamp
now
:=
timestamppb
.
Now
()
err
=
ts
.
UpdatePATLastUsed
(
ctx
,
user
.
ID
,
"pat-update-test"
,
now
)
require
.
NoError
(
t
,
err
)
// Verify the update
pats
,
err
:=
ts
.
GetUserPersonalAccessTokens
(
ctx
,
user
.
ID
)
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
pats
,
1
)
require
.
NotNil
(
t
,
pats
[
0
]
.
LastUsedAt
)
ts
.
Close
()
}
func
TestUserSettingGetUserByPATHashWithExpiredToken
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create a PAT with expiration info
patHash
:=
"pat-hash-with-expiry"
expiresAt
:=
timestamppb
.
Now
()
pat
:=
&
storepb
.
PersonalAccessTokensUserSetting_PersonalAccessToken
{
TokenId
:
"pat-expiry-test"
,
TokenHash
:
patHash
,
Description
:
"PAT with expiry"
,
ExpiresAt
:
expiresAt
,
}
err
=
ts
.
AddUserPersonalAccessToken
(
ctx
,
user
.
ID
,
pat
)
require
.
NoError
(
t
,
err
)
// Should still be able to look up by hash (expiry check is done at auth level)
result
,
err
:=
ts
.
GetUserByPATHash
(
ctx
,
patHash
)
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
result
)
require
.
Equal
(
t
,
user
.
ID
,
result
.
UserID
)
require
.
NotNil
(
t
,
result
.
PAT
.
ExpiresAt
)
ts
.
Close
()
}
func
TestUserSettingGetUserByPATHashAfterRemoval
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create a PAT
patHash
:=
"pat-hash-to-remove"
err
=
ts
.
AddUserPersonalAccessToken
(
ctx
,
user
.
ID
,
&
storepb
.
PersonalAccessTokensUserSetting_PersonalAccessToken
{
TokenId
:
"pat-remove-test"
,
TokenHash
:
patHash
,
Description
:
"PAT to be removed"
,
})
require
.
NoError
(
t
,
err
)
// Verify it exists
result
,
err
:=
ts
.
GetUserByPATHash
(
ctx
,
patHash
)
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
result
)
// Remove the PAT
err
=
ts
.
RemoveUserPersonalAccessToken
(
ctx
,
user
.
ID
,
"pat-remove-test"
)
require
.
NoError
(
t
,
err
)
// Should no longer be found
result
,
err
=
ts
.
GetUserByPATHash
(
ctx
,
patHash
)
require
.
Error
(
t
,
err
)
require
.
Nil
(
t
,
result
)
ts
.
Close
()
}
func
TestUserSettingGetUserByPATHashSpecialCharacters
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create PATs with special characters in hash (simulating real hash values)
testCases
:=
[]
struct
{
tokenID
string
tokenHash
string
}{
{
"pat-special-1"
,
"abc123+/=XYZ"
},
{
"pat-special-2"
,
"sha256:abcdef1234567890"
},
{
"pat-special-3"
,
"$2a$10$N9qo8uLOickgx2ZMRZoMy"
},
}
for
_
,
tc
:=
range
testCases
{
err
=
ts
.
AddUserPersonalAccessToken
(
ctx
,
user
.
ID
,
&
storepb
.
PersonalAccessTokensUserSetting_PersonalAccessToken
{
TokenId
:
tc
.
tokenID
,
TokenHash
:
tc
.
tokenHash
,
Description
:
"PAT with special chars"
,
})
require
.
NoError
(
t
,
err
)
// Verify lookup works with special characters
result
,
err
:=
ts
.
GetUserByPATHash
(
ctx
,
tc
.
tokenHash
)
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
result
)
require
.
Equal
(
t
,
tc
.
tokenID
,
result
.
PAT
.
TokenId
)
}
ts
.
Close
()
}
func
TestUserSettingGetUserByPATHashLargeTokenCount
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create many PATs for the same user
tokenCount
:=
10
hashes
:=
make
([]
string
,
tokenCount
)
for
i
:=
0
;
i
<
tokenCount
;
i
++
{
hashes
[
i
]
=
"pat-hash-"
+
string
(
rune
(
'A'
+
i
))
+
"-large-test"
err
=
ts
.
AddUserPersonalAccessToken
(
ctx
,
user
.
ID
,
&
storepb
.
PersonalAccessTokensUserSetting_PersonalAccessToken
{
TokenId
:
"pat-large-"
+
string
(
rune
(
'A'
+
i
)),
TokenHash
:
hashes
[
i
],
Description
:
"PAT for large count test"
,
})
require
.
NoError
(
t
,
err
)
}
// Verify each hash can be looked up
for
i
,
hash
:=
range
hashes
{
result
,
err
:=
ts
.
GetUserByPATHash
(
ctx
,
hash
)
require
.
NoError
(
t
,
err
)
require
.
NotNil
(
t
,
result
)
require
.
Equal
(
t
,
user
.
ID
,
result
.
UserID
)
require
.
Equal
(
t
,
"pat-large-"
+
string
(
rune
(
'A'
+
i
)),
result
.
PAT
.
TokenId
)
}
ts
.
Close
()
}
func
TestUserSettingMultipleSettingTypes
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ts
:=
NewTestingStore
(
ctx
,
t
)
user
,
err
:=
createTestingHostUser
(
ctx
,
ts
)
require
.
NoError
(
t
,
err
)
// Create GENERAL setting
_
,
err
=
ts
.
UpsertUserSetting
(
ctx
,
&
storepb
.
UserSetting
{
UserId
:
user
.
ID
,
Key
:
storepb
.
UserSetting_GENERAL
,
Value
:
&
storepb
.
UserSetting_General
{
General
:
&
storepb
.
GeneralUserSetting
{
Locale
:
"ja"
}},
})
require
.
NoError
(
t
,
err
)
// Create SHORTCUTS setting
_
,
err
=
ts
.
UpsertUserSetting
(
ctx
,
&
storepb
.
UserSetting
{
UserId
:
user
.
ID
,
Key
:
storepb
.
UserSetting_SHORTCUTS
,
Value
:
&
storepb
.
UserSetting_Shortcuts
{
Shortcuts
:
&
storepb
.
ShortcutsUserSetting
{
Shortcuts
:
[]
*
storepb
.
ShortcutsUserSetting_Shortcut
{
{
Id
:
"s1"
,
Title
:
"Shortcut 1"
},
},
}},
})
require
.
NoError
(
t
,
err
)
// Add a PAT
err
=
ts
.
AddUserPersonalAccessToken
(
ctx
,
user
.
ID
,
&
storepb
.
PersonalAccessTokensUserSetting_PersonalAccessToken
{
TokenId
:
"pat-multi"
,
TokenHash
:
"hash-multi"
,
})
require
.
NoError
(
t
,
err
)
// List all settings for user
settings
,
err
:=
ts
.
ListUserSettings
(
ctx
,
&
store
.
FindUserSetting
{
UserID
:
&
user
.
ID
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
settings
,
3
)
// Verify each setting type
generalSetting
,
err
:=
ts
.
GetUserSetting
(
ctx
,
&
store
.
FindUserSetting
{
UserID
:
&
user
.
ID
,
Key
:
storepb
.
UserSetting_GENERAL
})
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
"ja"
,
generalSetting
.
GetGeneral
()
.
Locale
)
shortcutsSetting
,
err
:=
ts
.
GetUserSetting
(
ctx
,
&
store
.
FindUserSetting
{
UserID
:
&
user
.
ID
,
Key
:
storepb
.
UserSetting_SHORTCUTS
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
shortcutsSetting
.
GetShortcuts
()
.
Shortcuts
,
1
)
patsSetting
,
err
:=
ts
.
GetUserSetting
(
ctx
,
&
store
.
FindUserSetting
{
UserID
:
&
user
.
ID
,
Key
:
storepb
.
UserSetting_PERSONAL_ACCESS_TOKENS
})
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
patsSetting
.
GetPersonalAccessTokens
()
.
Tokens
,
1
)
ts
.
Close
()
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment