Files
OwnCord/Server/service/message_perms_test.go
T
J3vbandClaude Fable 5 8787b9066d fix: batch of 34 correctness fixes across server and client (#1372)
* fix(client): 3 defect(s) (OC-0037, OC-0063, OC-0116)

Route the tray Status submenu through saveUserStatus() (mapping the legacy
"offline" to "invisible") so notifications, autoIdle, and reconnect presence
restore all agree with the tray's choice; build the connected overlay from
the auth_ok payload instead of a pre-dispatch authStore snapshot; keep the
TOTP overlay open across a rejected verify (totpPending latch) and retain
the partial token for the retry instead of clearing it in finally.

Hand-applied combined cluster preserved from the previous fix run's
overlap-guard block (both clusters edit main.ts).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(voice): 2 defect(s) (OC-0010, OC-0011)

* fix(ws): 1 defect(s) (OC-0050)

* fix(db): 1 defect(s) (OC-0052)

* fix(client): 1 defect(s) (OC-0054)

* fix(client): 1 defect(s) (OC-0059)

* fix(auth): 1 defect(s) (OC-0061)

* fix(ws): 1 defect(s) (OC-0062)

* fix(client): 1 defect(s) (OC-0064)

* fix(service): 1 defect(s) (OC-0070)

* fix(ws): 1 defect(s) (OC-0073)

* fix(service): 2 defect(s) (OC-0075, OC-0120)

* fix(admin): 1 defect(s) (OC-0076)

* fix(voice): 1 defect(s) (OC-0084)

* fix(client): 2 defect(s) (OC-0085, OC-0094)

Scope collapsed-category persistence to the connected host instead of the
server display name, and stop the DM back button from jumping to the first
text channel when DM mode was entered without recording channelBeforeDm.

* fix(service): 1 defect(s) (OC-0087)

* fix(client): 1 defect(s) (OC-0089)

* fix(ws): 1 defect(s) (OC-0091)

* fix(api): 1 defect(s) (OC-0093)

* fix(identity): 1 defect(s) (OC-0118)

* fix(dm): 1 defect(s) (OC-0119)

* fix(voice): 1 defect(s) (OC-0135)

* fix(api): 1 defect(s) (OC-0137)

* fix(client): 1 defect(s) (OC-0142)

* fix(client): 1 defect(s) (OC-0144)

* fix(admin): 1 defect(s) (OC-0145)

* fix(updater): 1 defect(s) (OC-0146)

* fix(client): 1 defect(s) (OC-0150)

* fix(mentions): 1 defect(s) (OC-0131)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 18:48:10 +02:00

54 lines
1.9 KiB
Go

package service
import (
"context"
"errors"
"testing"
"github.com/owncord/server/db"
"github.com/owncord/server/permissions"
)
// errDMChannelIDsStore wraps a real *db.DB but always fails
// GetUserDMChannelIDs, so GetAccessibleChannelIDs' fail-closed contract
// (OC-0087) is testable. Embedding *db.DB satisfies the service Store
// interface; only the overridden method diverges.
type errDMChannelIDsStore struct {
*db.DB
}
func (errDMChannelIDsStore) GetUserDMChannelIDs(context.Context, int64) ([]int64, error) {
return nil, errors.New("boom")
}
// TestGetAccessibleChannelIDs_DMLookupErrorFailsClosed locks OC-0087: a
// transient GetUserDMChannelIDs failure must not silently degrade the
// accessible-channel set to guild channels only. Before the fix the error was
// discarded (`if err == nil { ids = append(...) }`), so GetAccessibleChannelIDs
// returned (nil error, truncated set) and SearchMessages read back a
// successful-but-DM-stripped result — exactly the hole the ws sibling
// (computeAllowedChannels in ws/serve.go) was deliberately hardened against.
func TestGetAccessibleChannelIDs_DMLookupErrorFailsClosed(t *testing.T) {
database := newTestDB(t)
seedRole(t, database, &db.Role{
ID: permissions.MemberRoleID,
Name: "member",
Permissions: permissions.SendMessages | permissions.ReadMessages,
Position: 1,
})
seedUser(t, database, &db.User{ID: 1, Username: "alice"})
seedUserRole(t, database, 1, permissions.MemberRoleID)
seedChannel(t, database, &db.Channel{ID: 10, Name: "general", Type: "text"})
checker := permissions.NewChecker(database)
svc := NewMessageService(errDMChannelIDsStore{database}, NewPermissionService(database, checker), nil)
ids, err := svc.GetAccessibleChannelIDs(context.Background(), 1)
if err == nil {
t.Fatalf("expected an error when the DM lookup fails, got ids=%v, nil error", ids)
}
if !errors.Is(err, ErrInternal) {
t.Fatalf("error = %v, want ErrInternal", err)
}
}