lint: enable no extraneous class

This commit is contained in:
Peter Steinberger
2026-04-11 01:44:58 +01:00
parent c254ebfbef
commit 85c7748520
28 changed files with 84 additions and 93 deletions

View File

@@ -19,7 +19,7 @@
"oxc/no-map-spread": "off",
"typescript/consistent-return": "error",
"typescript/no-explicit-any": "error",
"typescript/no-extraneous-class": "off",
"typescript/no-extraneous-class": "error",
"typescript/no-unnecessary-type-conversion": "error",
"typescript/no-unsafe-type-assertion": "off",
"unicorn/consistent-function-scoping": "off",

View File

@@ -19,7 +19,7 @@ vi.mock("../runtime-api.js", () => ({
vi.mock("./runtime.js", () => ({
ACPX_BACKEND_ID: "acpx",
AcpxRuntime: class {},
AcpxRuntime: function AcpxRuntime() {},
createAgentRegistry: vi.fn(() => ({})),
createFileSessionStore: vi.fn(() => ({})),
}));

View File

@@ -52,23 +52,21 @@ vi.mock("@opentelemetry/sdk-node", () => ({
}));
vi.mock("@opentelemetry/exporter-metrics-otlp-proto", () => ({
OTLPMetricExporter: class {},
OTLPMetricExporter: function OTLPMetricExporter() {},
}));
vi.mock("@opentelemetry/exporter-trace-otlp-proto", () => ({
OTLPTraceExporter: class {
constructor(options?: unknown) {
traceExporterCtor(options);
}
OTLPTraceExporter: function OTLPTraceExporter(options?: unknown) {
traceExporterCtor(options);
},
}));
vi.mock("@opentelemetry/exporter-logs-otlp-proto", () => ({
OTLPLogExporter: class {},
OTLPLogExporter: function OTLPLogExporter() {},
}));
vi.mock("@opentelemetry/sdk-logs", () => ({
BatchLogRecordProcessor: class {},
BatchLogRecordProcessor: function BatchLogRecordProcessor() {},
LoggerProvider: class {
getLogger = vi.fn(() => ({
emit: logEmit,
@@ -78,19 +76,18 @@ vi.mock("@opentelemetry/sdk-logs", () => ({
}));
vi.mock("@opentelemetry/sdk-metrics", () => ({
PeriodicExportingMetricReader: class {},
PeriodicExportingMetricReader: function PeriodicExportingMetricReader() {},
}));
vi.mock("@opentelemetry/sdk-trace-base", () => ({
ParentBasedSampler: class {},
TraceIdRatioBasedSampler: class {},
ParentBasedSampler: function ParentBasedSampler() {},
TraceIdRatioBasedSampler: function TraceIdRatioBasedSampler() {},
}));
vi.mock("@opentelemetry/resources", () => ({
resourceFromAttributes: vi.fn((attrs: Record<string, unknown>) => attrs),
Resource: class {
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
constructor(_value?: unknown) {}
Resource: function Resource(_value?: unknown) {
// Constructor shape required by the mocked OpenTelemetry API.
},
}));

View File

@@ -65,7 +65,7 @@ export type {
} from "./components.types.js";
// Some test-only module graphs partially mock `@buape/carbon` and can drop `Modal`.
// Keep dynamic form definitions loadable instead of crashing unrelated suites.
const ModalBase: typeof Modal = Modal ?? class {};
const ModalBase: typeof Modal = Modal ?? (function ModalFallback() {} as unknown as typeof Modal);
export const DISCORD_COMPONENT_ATTACHMENT_PREFIX = "attachment://";

View File

@@ -72,7 +72,9 @@ beforeEach(() => {
});
describe("registerDiscordListener", () => {
class FakeListener {}
class FakeListener {
readonly testListener = true;
}
it("dedupes listeners by constructor", () => {
const listeners: object[] = [];

View File

@@ -92,22 +92,17 @@ vi.mock("https-proxy-agent", () => ({
}));
vi.mock("undici", () => ({
ProxyAgent: class {
proxyUrl: string;
constructor(proxyUrl: string) {
this.proxyUrl = proxyUrl;
undiciProxyAgentSpy(proxyUrl);
restProxyAgentSpy(proxyUrl);
}
ProxyAgent: function ProxyAgent(this: { proxyUrl: string }, proxyUrl: string) {
this.proxyUrl = proxyUrl;
undiciProxyAgentSpy(proxyUrl);
restProxyAgentSpy(proxyUrl);
},
fetch: undiciFetchMock,
}));
vi.mock("ws", () => ({
default: class MockWebSocket {
constructor(url: string, options?: { agent?: unknown }) {
webSocketSpy(url, options);
}
default: function MockWebSocket(url: string, options?: { agent?: unknown }) {
webSocketSpy(url, options);
},
}));
@@ -132,19 +127,14 @@ describe("createDiscordGatewayPlugin", () => {
return {
HttpsProxyAgentCtor:
HttpsProxyAgent as unknown as typeof import("https-proxy-agent").HttpsProxyAgent,
ProxyAgentCtor: class {
proxyUrl: string;
constructor(proxyUrl: string) {
this.proxyUrl = proxyUrl;
undiciProxyAgentSpy(proxyUrl);
restProxyAgentSpy(proxyUrl);
}
ProxyAgentCtor: function ProxyAgentCtor(this: { proxyUrl: string }, proxyUrl: string) {
this.proxyUrl = proxyUrl;
undiciProxyAgentSpy(proxyUrl);
restProxyAgentSpy(proxyUrl);
} as unknown as typeof import("undici").ProxyAgent,
undiciFetch: undiciFetchMock,
webSocketCtor: class {
constructor(url: string, options?: { agent?: unknown }) {
webSocketSpy(url, options);
}
webSocketCtor: function WebSocketCtor(url: string, options?: { agent?: unknown }) {
webSocketSpy(url, options);
} as unknown as new (url: string, options?: { agent?: unknown }) => import("ws").WebSocket,
registerClient: async (_plugin: unknown, client: unknown) => {
baseRegisterClientSpy(client);

View File

@@ -58,11 +58,11 @@ vi.mock("./gateway-supervisor.js", () => ({
}));
vi.mock("./listeners.js", () => ({
DiscordMessageListener: class DiscordMessageListener {},
DiscordPresenceListener: class DiscordPresenceListener {},
DiscordReactionListener: class DiscordReactionListener {},
DiscordReactionRemoveListener: class DiscordReactionRemoveListener {},
DiscordThreadUpdateListener: class DiscordThreadUpdateListener {},
DiscordMessageListener: function DiscordMessageListener() {},
DiscordPresenceListener: function DiscordPresenceListener() {},
DiscordReactionListener: function DiscordReactionListener() {},
DiscordReactionRemoveListener: function DiscordReactionRemoveListener() {},
DiscordThreadUpdateListener: function DiscordThreadUpdateListener() {},
registerDiscordListener: vi.fn(),
}));

View File

@@ -77,8 +77,8 @@ function createConfigWithDiscordAccount(overrides: Record<string, unknown> = {})
vi.mock("../voice/manager.runtime.js", () => {
voiceRuntimeModuleLoadedMock();
return {
DiscordVoiceManager: class DiscordVoiceManager {},
DiscordVoiceReadyListener: class DiscordVoiceReadyListener {},
DiscordVoiceManager: function DiscordVoiceManager() {},
DiscordVoiceReadyListener: function DiscordVoiceReadyListener() {},
};
});
describe("monitorDiscordProvider", () => {
@@ -173,8 +173,8 @@ describe("monitorDiscordProvider", () => {
providerTesting.setLoadDiscordVoiceRuntime(async () => {
voiceRuntimeModuleLoadedMock();
return {
DiscordVoiceManager: class DiscordVoiceManager {},
DiscordVoiceReadyListener: class DiscordVoiceReadyListener {},
DiscordVoiceManager: function DiscordVoiceManager() {},
DiscordVoiceReadyListener: function DiscordVoiceReadyListener() {},
} as never;
});
providerTesting.setLoadDiscordProviderSessionRuntime(

View File

@@ -341,7 +341,7 @@ vi.mock("@buape/carbon/gateway", () => ({
}));
vi.mock("@buape/carbon/voice", () => ({
VoicePlugin: class VoicePlugin {},
VoicePlugin: function VoicePlugin() {},
}));
vi.mock("openclaw/plugin-sdk/acp-runtime", async () => {
@@ -472,11 +472,11 @@ vi.mock(buildDiscordSourceModuleId("monitor/gateway-plugin.js"), () => ({
}));
vi.mock(buildDiscordSourceModuleId("monitor/listeners.js"), () => ({
DiscordMessageListener: class DiscordMessageListener {},
DiscordPresenceListener: class DiscordPresenceListener {},
DiscordReactionListener: class DiscordReactionListener {},
DiscordReactionRemoveListener: class DiscordReactionRemoveListener {},
DiscordThreadUpdateListener: class DiscordThreadUpdateListener {},
DiscordMessageListener: function DiscordMessageListener() {},
DiscordPresenceListener: function DiscordPresenceListener() {},
DiscordReactionListener: function DiscordReactionListener() {},
DiscordReactionRemoveListener: function DiscordReactionRemoveListener() {},
DiscordThreadUpdateListener: function DiscordThreadUpdateListener() {},
registerDiscordListener: vi.fn(),
}));

View File

@@ -27,7 +27,7 @@ vi.mock("../runtime-api.js", async () => {
});
vi.mock("google-auth-library", () => ({
GoogleAuth: class {},
GoogleAuth: function GoogleAuth() {},
OAuth2Client: class {
verifyIdToken = mocks.verifyIdToken;
},

View File

@@ -136,9 +136,9 @@ describe("performMatrixRequest", () => {
});
});
(globalThis as Record<string, unknown>)[TEST_UNDICI_RUNTIME_DEPS_KEY] = {
Agent: class MockAgent {},
EnvHttpProxyAgent: class MockEnvHttpProxyAgent {},
ProxyAgent: class MockProxyAgent {},
Agent: function MockAgent() {},
EnvHttpProxyAgent: function MockEnvHttpProxyAgent() {},
ProxyAgent: function MockProxyAgent() {},
fetch: runtimeFetch,
};

View File

@@ -36,7 +36,7 @@ export function createMatrixBotSdkMock(params: MatrixBotSdkMockParams = {}): Mat
warn = vi.fn();
error = vi.fn();
},
MatrixClient: params.matrixClient ?? class {},
MatrixClient: params.matrixClient ?? function MatrixClient() {},
LogService: {
setLogger: vi.fn(),
...(params.includeVerboseLogService
@@ -47,7 +47,9 @@ export function createMatrixBotSdkMock(params: MatrixBotSdkMockParams = {}): Mat
}
: {}),
},
SimpleFsStorageProvider: params.simpleFsStorageProvider ?? class {},
RustSdkCryptoStorageProvider: params.rustSdkCryptoStorageProvider ?? class {},
SimpleFsStorageProvider:
params.simpleFsStorageProvider ?? function SimpleFsStorageProvider() {},
RustSdkCryptoStorageProvider:
params.rustSdkCryptoStorageProvider ?? function RustSdkCryptoStorageProvider() {},
};
}

View File

@@ -104,8 +104,8 @@ const jwtValidate = vi.hoisted(() => vi.fn().mockResolvedValue(true));
const loadMSTeamsSdkWithAuth = vi.hoisted(() =>
vi.fn(async () => ({
sdk: {
ActivityHandler: class {},
MsalTokenProvider: class {},
ActivityHandler: function ActivityHandler() {},
MsalTokenProvider: function MsalTokenProvider() {},
authorizeJWT:
() => (_req: unknown, _res: unknown, next: ((err?: unknown) => void) | undefined) =>
next?.(),

View File

@@ -23,7 +23,7 @@ vi.mock("@microsoft/teams.apps", () => ({
}));
vi.mock("@microsoft/teams.api", () => ({
Client: class {},
Client: function Client() {},
}));
import { probeMSTeams } from "./probe.js";

View File

@@ -2,8 +2,8 @@ import { describe, expect, it } from "vitest";
import { __testing } from "./provider.js";
describe("resolveSlackBoltInterop", () => {
class FakeApp {}
class FakeHTTPReceiver {}
function FakeApp() {}
function FakeHTTPReceiver() {}
it("uses the default import when it already exposes named exports", () => {
const resolved = __testing.resolveSlackBoltInterop({

View File

@@ -59,7 +59,7 @@ vi.mock("grammy", () => ({
GrammyError: class GrammyError extends Error {
description = "";
},
InputFile: class {},
InputFile: function InputFile() {},
}));
let deleteMessageTelegram: typeof import("./send.js").deleteMessageTelegram;

View File

@@ -124,7 +124,7 @@ vi.mock("grammy", () => ({
GrammyError: class GrammyError extends Error {
description = "";
},
InputFile: class {},
InputFile: function InputFile() {},
}));
vi.mock("undici", () => ({

View File

@@ -47,7 +47,7 @@ vi.mock("@twurple/chat", () => ({
}));
vi.mock("@twurple/auth", () => ({
StaticAuthProvider: class {},
StaticAuthProvider: function StaticAuthProvider() {},
}));
describe("probeTwitch", () => {

View File

@@ -58,10 +58,8 @@ const mockAuthProvider = {
};
vi.mock("@twurple/auth", () => ({
StaticAuthProvider: class {
constructor(...args: unknown[]) {
mockAuthProvider.constructor(...args);
}
StaticAuthProvider: function StaticAuthProvider(...args: unknown[]) {
mockAuthProvider.constructor(...args);
},
RefreshingAuthProvider: class {
addUserForToken = mockAddUserForToken;

View File

@@ -56,11 +56,12 @@ class MockGatewayClient {
}
vi.mock("@agentclientprotocol/sdk", () => ({
AgentSideConnection: class {
constructor(factory: (conn: unknown) => unknown, stream: unknown) {
mockState.agentSideConnectionCtor(factory, stream);
factory({});
}
AgentSideConnection: function AgentSideConnection(
factory: (conn: unknown) => unknown,
stream: unknown,
) {
mockState.agentSideConnectionCtor(factory, stream);
factory({});
},
ndJsonStream: vi.fn(() => ({ type: "mock-stream" })),
}));

View File

@@ -39,7 +39,7 @@ export function mockCatalogImportFailThenRecover() {
}
return {
discoverAuthStorage: () => ({}),
AuthStorage: class {},
AuthStorage: function AuthStorage() {},
ModelRegistry: class {
getAll() {
return [{ id: "gpt-4.1", name: "GPT-4.1", provider: "openai" }];

View File

@@ -25,7 +25,7 @@ function mockCatalogImportFailThenRecover() {
}
return {
discoverAuthStorage: () => ({}),
AuthStorage: class {},
AuthStorage: function AuthStorage() {},
ModelRegistry: class {
getAll() {
return [{ id: "gpt-4.1", name: "GPT-4.1", provider: "openai" }];
@@ -41,7 +41,7 @@ function mockPiDiscoveryModels(models: unknown[]) {
async () =>
({
discoverAuthStorage: () => ({}),
AuthStorage: class {},
AuthStorage: function AuthStorage() {},
ModelRegistry: class {
getAll() {
return models;
@@ -118,7 +118,7 @@ describe("loadModelCatalog", () => {
async () =>
({
discoverAuthStorage: () => ({}),
AuthStorage: class {},
AuthStorage: function AuthStorage() {},
ModelRegistry: class {
getAll() {
return [

View File

@@ -230,8 +230,8 @@ export async function loadCompactHooksHarness(): Promise<{
});
vi.doMock("@mariozechner/pi-coding-agent", () => ({
AuthStorage: class AuthStorage {},
ModelRegistry: class ModelRegistry {},
AuthStorage: function AuthStorage() {},
ModelRegistry: function ModelRegistry() {},
createAgentSession: vi.fn(async () => {
const session = {
sessionId: "session-1",
@@ -261,7 +261,7 @@ export async function loadCompactHooksHarness(): Promise<{
};
return { session };
}),
DefaultResourceLoader: class DefaultResourceLoader {},
DefaultResourceLoader: function DefaultResourceLoader() {},
SessionManager: {
open: vi.fn(() => ({})),
},

View File

@@ -161,11 +161,11 @@ vi.mock("@mariozechner/pi-coding-agent", async () => {
const actual = await vi.importActual<typeof import("@mariozechner/pi-coding-agent")>(
"@mariozechner/pi-coding-agent",
);
class AuthStorage {}
function AuthStorage() {}
class DefaultResourceLoader {
async reload() {}
}
class ModelRegistry {}
function ModelRegistry() {}
return {
...actual,

View File

@@ -8,8 +8,8 @@ describe("pi-model-discovery module compatibility", () => {
it("loads when InMemoryAuthStorageBackend is not exported", async () => {
vi.resetModules();
vi.doMock("@mariozechner/pi-coding-agent", () => {
class MockAuthStorage {}
class MockModelRegistry {}
function MockAuthStorage() {}
function MockModelRegistry() {}
return {
AuthStorage: MockAuthStorage,

View File

@@ -277,8 +277,7 @@ export function createSubagentRunManager(params: {
const runId = registerParams.runId.trim();
const childSessionKey = registerParams.childSessionKey.trim();
const requesterSessionKey = registerParams.requesterSessionKey.trim();
const controllerSessionKey =
registerParams.controllerSessionKey?.trim() || requesterSessionKey;
const controllerSessionKey = registerParams.controllerSessionKey?.trim() || requesterSessionKey;
if (!runId || !childSessionKey || !requesterSessionKey) {
return;
}

View File

@@ -6,7 +6,9 @@ describe("isPlainObject", () => {
{},
{ a: 1 },
Object.create(null),
new (class X {})(),
new (class X {
readonly marker = true;
})(),
{ [Symbol.toStringTag]: "Object" },
])("accepts object-tag values: %j", (value) => {
expect(isPlainObject(value)).toBe(true);

View File

@@ -44,7 +44,7 @@ vi.mock("./controllers/sessions.ts", () => ({
subscribeSessions: vi.fn(),
}));
vi.mock("./gateway.ts", () => ({
GatewayBrowserClient: class {},
GatewayBrowserClient: function GatewayBrowserClient() {},
resolveGatewayErrorDetailCode: () => null,
}));