Apply repository-wide ESLint auto-fix formatting pass

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
2026-03-08 12:11:57 +08:00
parent 7c48faaa3d
commit 847904c49b
172 changed files with 13629 additions and 9154 deletions

View File

@@ -3,27 +3,27 @@
// Authors:
// imacat.yang@dsp.im (imacat), 2026/03/05
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { setActivePinia, createPinia } from 'pinia';
import { describe, it, expect, beforeEach, vi } from "vitest";
import { setActivePinia, createPinia } from "pinia";
// Mock apiError to prevent side effects (imports router, pinia, toast)
vi.mock('@/module/apiError.js', () => ({
vi.mock("@/module/apiError.js", () => ({
default: vi.fn(),
}));
import axios from 'axios';
import axios from "axios";
const { mockClientGet } = vi.hoisted(() => ({ mockClientGet: vi.fn() }));
vi.mock('@/api/client.js', () => ({
vi.mock("@/api/client.js", () => ({
default: { get: mockClientGet },
}));
import { useLoginStore } from '@/stores/login';
import { useLoginStore } from "@/stores/login";
// Mock axios methods (used for signIn/refreshToken which call plain axios)
vi.spyOn(axios, 'post').mockImplementation(vi.fn());
vi.spyOn(axios, "post").mockImplementation(vi.fn());
describe('loginStore', () => {
describe("loginStore", () => {
let store;
beforeEach(() => {
@@ -32,106 +32,106 @@ describe('loginStore', () => {
store.$router = { push: vi.fn() };
vi.clearAllMocks();
// Clear cookies
document.cookie.split(';').forEach((c) => {
const name = c.split('=')[0].trim();
document.cookie.split(";").forEach((c) => {
const name = c.split("=")[0].trim();
if (name) {
document.cookie = name + '=; Max-Age=-99999999; path=/';
document.cookie = name + "=; Max-Age=-99999999; path=/";
}
});
});
it('has correct default state', () => {
expect(store.auth.grant_type).toBe('password');
expect(store.auth.username).toBe('');
it("has correct default state", () => {
expect(store.auth.grant_type).toBe("password");
expect(store.auth.username).toBe("");
expect(store.isLoggedIn).toBe(false);
expect(store.isInvalid).toBe(false);
});
describe('signIn', () => {
it('stores token and navigates on success', async () => {
describe("signIn", () => {
it("stores token and navigates on success", async () => {
axios.post.mockResolvedValue({
data: {
access_token: 'test-access-token',
refresh_token: 'test-refresh-token',
access_token: "test-access-token",
refresh_token: "test-refresh-token",
},
});
await store.signIn();
expect(axios.post).toHaveBeenCalledWith(
'/api/oauth/token',
"/api/oauth/token",
store.auth,
expect.objectContaining({
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
headers: { "Content-Type": "application/x-www-form-urlencoded" },
}),
);
expect(store.isLoggedIn).toBe(true);
// Verify token cookie was set with Secure flag
// (jsdom drops Secure cookies, so spy on setter)
const cookieSetter = vi.spyOn(document, 'cookie', 'set');
const cookieSetter = vi.spyOn(document, "cookie", "set");
vi.clearAllMocks();
axios.post.mockResolvedValue({
data: {
access_token: 'test-access-token',
refresh_token: 'test-refresh-token',
access_token: "test-access-token",
refresh_token: "test-refresh-token",
},
});
await store.signIn();
const tokenCall = cookieSetter.mock.calls.find(
(c) => c[0].includes('luciaToken='),
const tokenCall = cookieSetter.mock.calls.find((c) =>
c[0].includes("luciaToken="),
);
expect(tokenCall).toBeDefined();
expect(tokenCall[0]).toContain('Secure');
expect(tokenCall[0]).toContain("Secure");
cookieSetter.mockRestore();
expect(store.$router.push).toHaveBeenCalledWith('/files');
expect(store.$router.push).toHaveBeenCalledWith("/files");
});
it('redirects to remembered URL when set', async () => {
it("redirects to remembered URL when set", async () => {
axios.post.mockResolvedValue({
data: {
access_token: 'token',
refresh_token: 'refresh',
access_token: "token",
refresh_token: "refresh",
},
});
// btoa('/dashboard') = 'L2Rhc2hib2FyZA=='
store.rememberedReturnToUrl = btoa('/dashboard');
store.rememberedReturnToUrl = btoa("/dashboard");
// Mock window.location.href setter
const originalLocation = window.location;
delete window.location;
window.location = { href: '' };
window.location = { href: "" };
await store.signIn();
expect(window.location.href).toBe('/dashboard');
expect(window.location.href).toBe("/dashboard");
window.location = originalLocation;
});
it('does not redirect to external URL (open redirect prevention)', async () => {
it("does not redirect to external URL (open redirect prevention)", async () => {
axios.post.mockResolvedValue({
data: {
access_token: 'token',
refresh_token: 'refresh',
access_token: "token",
refresh_token: "refresh",
},
});
// Attacker crafts a return-to URL pointing to an external site
store.rememberedReturnToUrl = btoa('https://evil.example.com/steal');
store.rememberedReturnToUrl = btoa("https://evil.example.com/steal");
const originalLocation = window.location;
delete window.location;
window.location = { href: '' };
window.location = { href: "" };
await store.signIn();
// Should NOT redirect to the external URL
expect(window.location.href).not.toBe('https://evil.example.com/steal');
expect(window.location.href).not.toBe("https://evil.example.com/steal");
// Should fall back to /files
expect(store.$router.push).toHaveBeenCalledWith('/files');
expect(store.$router.push).toHaveBeenCalledWith("/files");
window.location = originalLocation;
});
it('sets isInvalid on error', async () => {
axios.post.mockRejectedValue(new Error('401'));
it("sets isInvalid on error", async () => {
axios.post.mockRejectedValue(new Error("401"));
await store.signIn();
@@ -139,34 +139,34 @@ describe('loginStore', () => {
});
});
describe('logOut', () => {
it('clears auth state and navigates to login', () => {
describe("logOut", () => {
it("clears auth state and navigates to login", () => {
store.isLoggedIn = true;
store.logOut();
expect(store.isLoggedIn).toBe(false);
expect(store.$router.push).toHaveBeenCalledWith('/login');
expect(store.$router.push).toHaveBeenCalledWith("/login");
});
});
describe('getUserData', () => {
it('stores user data on success', async () => {
describe("getUserData", () => {
it("stores user data on success", async () => {
mockClientGet.mockResolvedValue({
data: { username: 'testuser', name: 'Test User' },
data: { username: "testuser", name: "Test User" },
});
await store.getUserData();
expect(mockClientGet).toHaveBeenCalledWith('/api/my-account');
expect(mockClientGet).toHaveBeenCalledWith("/api/my-account");
expect(store.userData).toEqual({
username: 'testuser',
name: 'Test User',
username: "testuser",
name: "Test User",
});
});
});
describe('checkLogin', () => {
it('does not redirect on success', async () => {
describe("checkLogin", () => {
it("does not redirect on success", async () => {
mockClientGet.mockResolvedValue({ data: {} });
await store.checkLogin();
@@ -174,34 +174,34 @@ describe('loginStore', () => {
expect(store.$router.push).not.toHaveBeenCalled();
});
it('redirects to login on error', async () => {
mockClientGet.mockRejectedValue(new Error('401'));
it("redirects to login on error", async () => {
mockClientGet.mockRejectedValue(new Error("401"));
await store.checkLogin();
expect(store.$router.push).toHaveBeenCalledWith('/login');
expect(store.$router.push).toHaveBeenCalledWith("/login");
});
});
it('setRememberedReturnToUrl stores URL', () => {
store.setRememberedReturnToUrl('abc');
expect(store.rememberedReturnToUrl).toBe('abc');
it("setRememberedReturnToUrl stores URL", () => {
store.setRememberedReturnToUrl("abc");
expect(store.rememberedReturnToUrl).toBe("abc");
});
it('setIsLoggedIn sets boolean', () => {
it("setIsLoggedIn sets boolean", () => {
store.setIsLoggedIn(true);
expect(store.isLoggedIn).toBe(true);
});
describe('refreshToken', () => {
it('sends request with correct config and updates tokens on success', async () => {
document.cookie = 'luciaRefreshToken=old-refresh-token';
describe("refreshToken", () => {
it("sends request with correct config and updates tokens on success", async () => {
document.cookie = "luciaRefreshToken=old-refresh-token";
axios.post.mockResolvedValue({
status: 200,
data: {
access_token: 'new-access-token',
refresh_token: 'new-refresh-token',
access_token: "new-access-token",
refresh_token: "new-refresh-token",
},
});
@@ -209,44 +209,43 @@ describe('loginStore', () => {
// Should call with content-type header (config must be defined)
expect(axios.post).toHaveBeenCalledWith(
'/api/oauth/token',
"/api/oauth/token",
expect.objectContaining({
grant_type: 'refresh_token',
refresh_token: 'old-refresh-token',
grant_type: "refresh_token",
refresh_token: "old-refresh-token",
}),
expect.objectContaining({
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
headers: { "Content-Type": "application/x-www-form-urlencoded" },
}),
);
// Verify cookies were set with Secure flag
const cookieSetter = vi.spyOn(document, 'cookie', 'set');
const cookieSetter = vi.spyOn(document, "cookie", "set");
vi.clearAllMocks();
document.cookie = 'luciaRefreshToken=old-refresh-token';
document.cookie = "luciaRefreshToken=old-refresh-token";
axios.post.mockResolvedValue({
status: 200,
data: {
access_token: 'new-access-token',
refresh_token: 'new-refresh-token',
access_token: "new-access-token",
refresh_token: "new-refresh-token",
},
});
await store.refreshToken();
const tokenCall = cookieSetter.mock.calls.find(
(c) => c[0].includes('luciaToken='),
const tokenCall = cookieSetter.mock.calls.find((c) =>
c[0].includes("luciaToken="),
);
expect(tokenCall).toBeDefined();
expect(tokenCall[0]).toContain('Secure');
expect(tokenCall[0]).toContain("Secure");
cookieSetter.mockRestore();
});
it('redirects to login and re-throws on failure', async () => {
document.cookie = 'luciaRefreshToken=old-refresh-token';
axios.post.mockRejectedValue(new Error('401'));
it("redirects to login and re-throws on failure", async () => {
document.cookie = "luciaRefreshToken=old-refresh-token";
axios.post.mockRejectedValue(new Error("401"));
await expect(store.refreshToken()).rejects.toThrow('401');
await expect(store.refreshToken()).rejects.toThrow("401");
expect(store.$router.push).toHaveBeenCalledWith('/login');
expect(store.$router.push).toHaveBeenCalledWith("/login");
});
});
});