mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
Deletes Server/store (SQLiteStore, MemStore, the composed Store interface) and collapses to a single sqlc-backed db package, executing the prior audit's P4 "single data layer" direction (finding #6). SQLiteStore was a pure pass-through to *db.DB, so consumers now depend on narrow interfaces that *db.DB satisfies directly: - service.Store (service/datastore.go, renamed from store/store.go) - ws.EventStore (ws/eventstore.go) - plugin.PluginStore (plugin/pluginstore.go) The event- and plugin-KV methods that lived in the store's SQLite implementation move into the db package (db/event_queries.go, db/plugin_queries.go), keeping their raw-SQL form. Tests: the MemStore-based unit tests now run against a real in-memory SQLite db opened per-test with migrations applied, via package-local seed helpers. Fault-injection tests embed a real *db.DB and override the single method under test, preserving error-path coverage. Full server suite and sqlc-verify are green. Docs: audit finding #6 and A-2026-07-06 marked resolved; decisions D3 updated; architecture server.md / data-model.md diagrams and prose updated to the api -> service -> db layering. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UA17KPvqGBX3XbXYnMf1rA
193 lines
6.6 KiB
Go
193 lines
6.6 KiB
Go
package service
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/owncord/server/db"
|
|
"github.com/owncord/server/permissions"
|
|
)
|
|
|
|
// newTestPermService creates a PermissionService backed by a real in-memory DB
|
|
// pre-populated with a single role and user.
|
|
func newTestPermService(t *testing.T) (*PermissionService, *db.DB) {
|
|
t.Helper()
|
|
database := newTestDB(t)
|
|
seedRole(t, database, &db.Role{
|
|
ID: permissions.MemberRoleID,
|
|
Name: "member",
|
|
Permissions: permissions.SendMessages | permissions.ReadMessages | permissions.AddReactions,
|
|
Position: 1,
|
|
})
|
|
seedUserRole(t, database, 1, permissions.MemberRoleID)
|
|
checker := permissions.NewChecker(database)
|
|
return NewPermissionService(database, checker), database
|
|
}
|
|
|
|
func TestHasChannelPerm_Allowed(t *testing.T) {
|
|
svc, database := newTestPermService(t)
|
|
seedChannel(t, database, &db.Channel{ID: 10, Name: "general", Type: "text"})
|
|
|
|
// Member has SendMessages | ReadMessages; no overrides exist, so base role perms apply.
|
|
if !svc.HasChannelPerm(1, 10, permissions.SendMessages) {
|
|
t.Fatal("expected user to have SendMessages permission")
|
|
}
|
|
if !svc.HasChannelPerm(1, 10, permissions.ReadMessages) {
|
|
t.Fatal("expected user to have ReadMessages permission")
|
|
}
|
|
}
|
|
|
|
func TestHasChannelPerm_Denied(t *testing.T) {
|
|
svc, database := newTestPermService(t)
|
|
seedChannel(t, database, &db.Channel{ID: 10, Name: "general", Type: "text"})
|
|
|
|
// ManageMessages is NOT in the member role.
|
|
if svc.HasChannelPerm(1, 10, permissions.ManageMessages) {
|
|
t.Fatal("expected user to NOT have ManageMessages permission")
|
|
}
|
|
}
|
|
|
|
func TestHasChannelPerm_OverrideDeny(t *testing.T) {
|
|
svc, database := newTestPermService(t)
|
|
seedChannel(t, database, &db.Channel{ID: 10, Name: "readonly", Type: "text"})
|
|
// Deny SendMessages for this channel.
|
|
seedChannelOverride(t, database, permissions.MemberRoleID, 10, 0, permissions.SendMessages)
|
|
// Invalidate so next check re-populates cache.
|
|
svc.InvalidateAll()
|
|
|
|
if svc.HasChannelPerm(1, 10, permissions.SendMessages) {
|
|
t.Fatal("expected SendMessages to be denied via channel override")
|
|
}
|
|
// ReadMessages should still be allowed.
|
|
if !svc.HasChannelPerm(1, 10, permissions.ReadMessages) {
|
|
t.Fatal("expected ReadMessages to remain allowed")
|
|
}
|
|
}
|
|
|
|
func TestHasChannelPerm_OverrideAllow(t *testing.T) {
|
|
svc, database := newTestPermService(t)
|
|
seedChannel(t, database, &db.Channel{ID: 10, Name: "special", Type: "text"})
|
|
// Allow ManageMessages (not in base role) via channel override.
|
|
seedChannelOverride(t, database, permissions.MemberRoleID, 10, permissions.ManageMessages, 0)
|
|
svc.InvalidateAll()
|
|
|
|
if !svc.HasChannelPerm(1, 10, permissions.ManageMessages) {
|
|
t.Fatal("expected ManageMessages to be allowed via channel override")
|
|
}
|
|
}
|
|
|
|
func TestHasChannelPerm_AdminBypass(t *testing.T) {
|
|
database := newTestDB(t)
|
|
seedRole(t, database, &db.Role{
|
|
ID: permissions.AdminRoleID,
|
|
Name: "admin",
|
|
Permissions: permissions.Administrator,
|
|
Position: 90,
|
|
})
|
|
seedUserRole(t, database, 1, permissions.AdminRoleID)
|
|
checker := permissions.NewChecker(database)
|
|
svc := NewPermissionService(database, checker)
|
|
|
|
seedChannel(t, database, &db.Channel{ID: 10, Name: "locked", Type: "text"})
|
|
// Deny everything via override; admin should still bypass.
|
|
seedChannelOverride(t, database, permissions.AdminRoleID, 10, 0, permissions.SendMessages|permissions.ReadMessages)
|
|
|
|
if !svc.HasChannelPerm(1, 10, permissions.SendMessages) {
|
|
t.Fatal("admin should bypass all permission checks")
|
|
}
|
|
if !svc.HasChannelPerm(1, 10, permissions.ManageMessages) {
|
|
t.Fatal("admin should bypass all permission checks")
|
|
}
|
|
}
|
|
|
|
func TestInvalidateUser_ClearsCacheForUser(t *testing.T) {
|
|
svc, database := newTestPermService(t)
|
|
seedChannel(t, database, &db.Channel{ID: 10, Name: "general", Type: "text"})
|
|
|
|
// Populate cache.
|
|
svc.HasChannelPerm(1, 10, permissions.SendMessages)
|
|
|
|
// Now add a deny override.
|
|
seedChannelOverride(t, database, permissions.MemberRoleID, 10, 0, permissions.SendMessages)
|
|
|
|
// Without invalidation, cache still says allowed.
|
|
if !svc.HasChannelPerm(1, 10, permissions.SendMessages) {
|
|
t.Fatal("expected cached value to still allow SendMessages")
|
|
}
|
|
|
|
// After invalidation, should pick up the override.
|
|
svc.InvalidateUser(1)
|
|
if svc.HasChannelPerm(1, 10, permissions.SendMessages) {
|
|
t.Fatal("expected SendMessages to be denied after cache invalidation")
|
|
}
|
|
}
|
|
|
|
func TestInvalidateAll_ClearsEntireCache(t *testing.T) {
|
|
svc, database := newTestPermService(t)
|
|
// Add a second user.
|
|
seedUserRole(t, database, 2, permissions.MemberRoleID)
|
|
seedChannel(t, database, &db.Channel{ID: 10, Name: "general", Type: "text"})
|
|
|
|
// Populate cache for both users.
|
|
svc.HasChannelPerm(1, 10, permissions.SendMessages)
|
|
svc.HasChannelPerm(2, 10, permissions.SendMessages)
|
|
|
|
// Add deny override.
|
|
seedChannelOverride(t, database, permissions.MemberRoleID, 10, 0, permissions.SendMessages)
|
|
|
|
// Both still cached as allowed.
|
|
if !svc.HasChannelPerm(1, 10, permissions.SendMessages) {
|
|
t.Fatal("expected cached allow for user 1")
|
|
}
|
|
if !svc.HasChannelPerm(2, 10, permissions.SendMessages) {
|
|
t.Fatal("expected cached allow for user 2")
|
|
}
|
|
|
|
svc.InvalidateAll()
|
|
|
|
// Both should now see the deny.
|
|
if svc.HasChannelPerm(1, 10, permissions.SendMessages) {
|
|
t.Fatal("expected deny for user 1 after InvalidateAll")
|
|
}
|
|
if svc.HasChannelPerm(2, 10, permissions.SendMessages) {
|
|
t.Fatal("expected deny for user 2 after InvalidateAll")
|
|
}
|
|
}
|
|
|
|
func TestPermCacheTTLExpiry(t *testing.T) {
|
|
// This test verifies the cache TTL mechanism. We cannot easily wait 30s
|
|
// in a unit test, so we verify the structural behavior: after manually
|
|
// backdating the populatedAt field the cache should be stale and the
|
|
// next check should re-populate from the store.
|
|
svc, database := newTestPermService(t)
|
|
seedChannel(t, database, &db.Channel{ID: 10, Name: "general", Type: "text"})
|
|
|
|
// Populate cache.
|
|
svc.HasChannelPerm(1, 10, permissions.SendMessages)
|
|
|
|
// Add deny override.
|
|
seedChannelOverride(t, database, permissions.MemberRoleID, 10, 0, permissions.SendMessages)
|
|
|
|
// Manually expire the cache entry by backdating populatedAt.
|
|
svc.mu.Lock()
|
|
if cp, ok := svc.cache[int64(1)]; ok {
|
|
cp.populatedAt = time.Now().Add(-permCacheTTL - time.Second)
|
|
}
|
|
svc.mu.Unlock()
|
|
|
|
// The next call should re-populate and pick up the deny.
|
|
if svc.HasChannelPerm(1, 10, permissions.SendMessages) {
|
|
t.Fatal("expected cache TTL expiry to cause re-population with deny override")
|
|
}
|
|
}
|
|
|
|
func TestHasChannelPerm_UnknownUserReturnsFalse(t *testing.T) {
|
|
svc, database := newTestPermService(t)
|
|
seedChannel(t, database, &db.Channel{ID: 10, Name: "general", Type: "text"})
|
|
|
|
// User 999 has no role assigned.
|
|
if svc.HasChannelPerm(999, 10, permissions.SendMessages) {
|
|
t.Fatal("expected false for unknown user")
|
|
}
|
|
}
|