This repository has been archived by the owner on Jul 28, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* fix server tests * shared tests * server tests * cleanup
- Loading branch information
Showing
16 changed files
with
435 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,6 @@ | ||
// Required for node-js sdk dependency (jose) | ||
import { TextEncoder, TextDecoder } from 'util'; | ||
Object.assign(global, { TextDecoder, TextEncoder }); | ||
|
||
// Mock fetch | ||
require('jest-fetch-mock').enableMocks(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import { NextRequest, NextResponse } from 'next/server'; | ||
import authMiddleware from '../../src/server/authMiddleware'; | ||
import { DEFAULT_PUBLIC_ROUTES } from '../../src/server/constants'; | ||
|
||
const mockValidateJwt = jest.fn(); | ||
jest.mock('@descope/node-sdk', () => | ||
jest.fn(() => ({ | ||
validateJwt: mockValidateJwt | ||
})) | ||
); | ||
|
||
jest.mock('next/server', () => ({ | ||
NextResponse: { | ||
redirect: jest.fn(), | ||
next: jest.fn() | ||
} | ||
})); | ||
|
||
// Utility function to create a mock NextRequest | ||
const createMockNextRequest = ( | ||
options: { | ||
headers?: Record<string, string>; | ||
cookies?: Record<string, string>; | ||
pathname?: string; | ||
} = {} | ||
) => | ||
({ | ||
headers: { | ||
get: (name: string) => options.headers?.[name] | ||
}, | ||
cookies: { | ||
get: (name: string) => ({ value: options.cookies?.[name] }) | ||
}, | ||
nextUrl: { | ||
pathname: options.pathname || '/', | ||
clone: jest.fn(() => ({ pathname: options.pathname || '/' })) | ||
} | ||
}) as unknown as NextRequest; | ||
|
||
describe('authMiddleware', () => { | ||
beforeEach(() => { | ||
// Set process.env.DESCOPE_PROJECT_ID to avoid errors | ||
process.env.DESCOPE_PROJECT_ID = 'project1'; | ||
(NextResponse.redirect as jest.Mock).mockImplementation((url) => url); | ||
(NextResponse.next as jest.Mock).mockImplementation(() => ({ | ||
headers: { set: jest.fn() }, | ||
request: jest.fn() | ||
})); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.resetAllMocks(); | ||
(NextResponse.redirect as jest.Mock).mockReset(); | ||
(NextResponse.next as jest.Mock).mockReset(); | ||
mockValidateJwt?.mockReset(); | ||
}); | ||
|
||
it('redirects unauthenticated users for non-public routes', async () => { | ||
mockValidateJwt.mockRejectedValue(new Error('Invalid JWT')); | ||
const middleware = authMiddleware(); | ||
const mockReq = createMockNextRequest({ pathname: '/private' }); | ||
|
||
const response = await middleware(mockReq); | ||
expect(NextResponse.redirect).toHaveBeenCalledWith(expect.anything()); | ||
expect(response).toEqual({ | ||
pathname: DEFAULT_PUBLIC_ROUTES.signIn | ||
}); | ||
}); | ||
|
||
it('allows unauthenticated users for public routes', async () => { | ||
mockValidateJwt.mockRejectedValue(new Error('Invalid JWT')); | ||
const middleware = authMiddleware({ | ||
publicRoutes: ['/sign-in', '/sign-up'] | ||
}); | ||
const mockReq = createMockNextRequest({ pathname: '/sign-in' }); | ||
|
||
await middleware(mockReq); | ||
// Expect the middleware not to redirect | ||
expect(NextResponse.redirect).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('allows authenticated users for non-public routes and adds proper headers', async () => { | ||
// Mock validateJwt to simulate an authenticated user | ||
const authInfo = { | ||
jwt: 'validJwt', | ||
token: { iss: 'project-1', sub: 'user-123' } | ||
}; | ||
mockValidateJwt.mockImplementation(() => authInfo); | ||
|
||
const middleware = authMiddleware(); | ||
const mockReq = createMockNextRequest({ | ||
pathname: '/private', | ||
headers: { Authorization: 'Bearer validJwt' } | ||
}); | ||
|
||
await middleware(mockReq); | ||
// Expect no redirect and check if response contains session headers | ||
expect(NextResponse.redirect).not.toHaveBeenCalled(); | ||
expect(NextResponse.next).toHaveBeenCalled(); | ||
|
||
const headersArg = (NextResponse.next as any as jest.Mock).mock.lastCall[0] | ||
.request.headers; | ||
expect(headersArg.get('x-descope-session')).toEqual( | ||
Buffer.from(JSON.stringify(authInfo)).toString('base64') | ||
); | ||
}); | ||
|
||
it('blocks unauthenticated users and redirects to custom URL', async () => { | ||
mockValidateJwt.mockRejectedValue(new Error('Invalid JWT')); | ||
const customRedirectUrl = '/custom-sign-in'; | ||
const middleware = authMiddleware({ redirectUrl: customRedirectUrl }); | ||
const mockReq = createMockNextRequest({ pathname: '/private' }); | ||
|
||
await middleware(mockReq); | ||
expect(NextResponse.redirect).toHaveBeenCalledWith({ | ||
pathname: customRedirectUrl | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import descopeSdk from '@descope/node-sdk'; | ||
import { baseHeaders } from '../../src/server/constants'; | ||
import { createSdk, getGlobalSdk } from '../../src/server/sdk'; | ||
|
||
jest.mock('@descope/node-sdk', () => jest.fn()); | ||
|
||
describe('sdk', () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.resetModules(); | ||
}); | ||
|
||
describe('createSdk', () => { | ||
it('should create a new sdk with parameters', () => { | ||
const config = { projectId: 'project1', managementKey: 'key1' }; | ||
createSdk(config); | ||
|
||
expect(descopeSdk).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
projectId: 'project1', | ||
managementKey: 'key1', | ||
baseHeaders: expect.objectContaining(baseHeaders) | ||
}) | ||
); | ||
}); | ||
|
||
it('should create a new sdk with env variables', () => { | ||
process.env.DESCOPE_PROJECT_ID = 'envProjectId'; | ||
process.env.DESCOPE_MANAGEMENT_KEY = 'envManagementKey'; | ||
process.env.DESCOPE_BASE_URL = 'envBaseUrl'; | ||
|
||
createSdk(); | ||
|
||
expect(descopeSdk).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
projectId: 'envProjectId', | ||
managementKey: 'envManagementKey', | ||
baseUrl: 'envBaseUrl', | ||
baseHeaders: expect.any(Object) | ||
}) | ||
); | ||
|
||
// Clean up environment variables to avoid side effects | ||
delete process.env.DESCOPE_PROJECT_ID; | ||
delete process.env.DESCOPE_MANAGEMENT_KEY; | ||
delete process.env.DESCOPE_BASE_URL; | ||
}); | ||
}); | ||
|
||
describe('getGlobalSdk', () => { | ||
it('should create a new sdk if one does not exist', () => { | ||
const config = { projectId: 'project1' }; | ||
getGlobalSdk(config); | ||
|
||
expect(descopeSdk).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
projectId: 'project1' | ||
}) | ||
); | ||
}); | ||
|
||
it('should return the existing sdk', () => { | ||
const sdk1 = getGlobalSdk({ projectId: 'project1' }); | ||
const sdk2 = getGlobalSdk({ projectId: 'project1' }); | ||
|
||
expect(sdk1).toBe(sdk2); // Verify that the same SDK instance is returned | ||
}); | ||
|
||
it("should throw an error if no projectId is provided and it's not in env", () => { | ||
// environment variable is not set and no projectId is provided | ||
delete process.env.DESCOPE_PROJECT_ID; | ||
|
||
expect(() => getGlobalSdk()).toThrow( | ||
'Descope project ID is required to create the SDK' | ||
); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.