feat: add allowed Linear API operations to MCP config

This commit is contained in:
Do Siki
2025-09-05 03:10:17 +02:00
parent 7e7d3fb1cc
commit b6365543ef
36 changed files with 14648 additions and 1 deletions
+302
View File
@@ -0,0 +1,302 @@
// No need to require jest - it's available globally in Jest test files
// JEST MOCKS - must be set up before importing any modules that use them
// Mock MongoDB - fix MongoClient to work properly
const mockAdmin = {
ping: jest.fn().mockResolvedValue({ ok: 1 })
}
const mockDb = {
collection: jest.fn().mockReturnValue({
findOne: jest.fn().mockResolvedValue(null),
replaceOne: jest.fn().mockResolvedValue({ acknowledged: true }),
find: jest.fn(() => ({
toArray: jest.fn().mockResolvedValue([])
})),
insertOne: jest.fn().mockResolvedValue({ acknowledged: true, insertedId: 'test-id' }),
insertMany: jest.fn().mockResolvedValue({ acknowledged: true, insertedIds: { 0: 'test-id' } }),
updateOne: jest.fn().mockResolvedValue({ acknowledged: true, matchedCount: 1, modifiedCount: 1 }),
updateMany: jest.fn().mockResolvedValue({ acknowledged: true, matchedCount: 2, modifiedCount: 2 }),
deleteOne: jest.fn().mockResolvedValue({ acknowledged: true, deletedCount: 1 }),
deleteMany: jest.fn().mockResolvedValue({ acknowledged: true, deletedCount: 2 }),
bulkWrite: jest.fn().mockResolvedValue({ acknowledged: true, insertedCount: 0, matchedCount: 0, modifiedCount: 0, deletedCount: 0, upsertedCount: 0, upsertedIds: {} })
}),
admin: jest.fn().mockReturnValue(mockAdmin)
}
const mockClient = {
connect: jest.fn().mockResolvedValue({}),
close: jest.fn().mockResolvedValue(undefined),
db: jest.fn().mockImplementation((dbName) => mockDb)
}
jest.mock('mongodb', () => ({
MongoClient: jest.fn().mockImplementation(() => mockClient)
}))
// Mock environment variables for tests
process.env.MONGODB_URI = 'mongodb://localhost:27017/test'
process.env.MONGODB_DB = 'test'
// Mock Winston logger - simplified for default export
jest.mock('winston', () => ({
transports: {
Console: jest.fn(),
},
createLogger: jest.fn(() => ({
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
log: jest.fn(),
child: jest.fn(() => ({
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
log: jest.fn(),
isLevelEnabled: jest.fn().mockReturnValue(true),
levels: { error: 0, warn: 1, info: 2, debug: 3 }
})),
isLevelEnabled: jest.fn().mockReturnValue(true),
levels: { error: 0, warn: 1, info: 2, debug: 3 }
})),
format: {
timestamp: jest.fn(() => ({})),
errors: jest.fn(() => ({})),
json: jest.fn(() => ({})),
colorize: jest.fn(() => ({})),
simple: jest.fn(() => ({})),
combine: jest.fn((...args) => args[args.length - 1] || {}),
printf: jest.fn(() => ({}))
}
}))
// Mock logger module to use the mocked winston
jest.mock('./src/lib/logger', () => {
const mockLogger = {
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
log: jest.fn(),
child: jest.fn(() => ({
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
log: jest.fn()
}))
}
return {
__esModule: true,
default: mockLogger,
logger: mockLogger,
createComponentLogger: jest.fn(() => mockLogger.child()),
requestLogger: mockLogger.child(),
apiLogger: mockLogger.child(),
dbLogger: mockLogger.child(),
authLogger: mockLogger.child(),
errorLogger: mockLogger.child(),
generateRequestId: jest.fn(() => 'test-request-id'),
withTiming: jest.fn(async (operation, fn) => fn())
}
})
// Mock Next.js Response objects
global.Response = class Response {
constructor(body, init) {
this.body = body
this.status = init?.status || 200
this.statusText = init?.statusText || ''
this.headers = new Map()
if (init?.headers) {
Object.entries(init.headers).forEach(([key, value]) => {
this.headers.set(key.toLowerCase(), value)
})
}
}
json() {
return Promise.resolve(JSON.parse(this.body || '{}'))
}
headers = {
get: (key) => this.headers.get(key.toLowerCase())
}
}
global.NextResponse = {
json: (data, init) => {
return new Response(JSON.stringify(data), {
status: init?.status || 200,
headers: init?.headers || {}
})
}
}
// Mock Next.js NextResponse properly
jest.mock('next/server', () => ({
NextResponse: {
json: jest.fn((data, init) => {
return new Response(JSON.stringify(data), {
status: init?.status || 200,
headers: init?.headers || {}
})
})
},
NextRequest: jest.fn()
}))
// Ensure process.env is available in tests
if (!global.process.env) {
global.process.env = {}
}
// Logger will be mocked by the winston mock above
// Mock Loki transport
jest.mock('winston-loki', () => jest.fn(() => ({
log: jest.fn(),
close: jest.fn()
})))
// Mock Next.js router
jest.mock('next/router', () => ({
useRouter: () => ({
route: '/',
pathname: '/',
query: {},
asPath: '/',
push: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),
back: jest.fn(),
prefetch: jest.fn(),
beforePopState: jest.fn(),
events: {
on: jest.fn(),
off: jest.fn(),
emit: jest.fn(),
},
}),
}))
// Mock Next.js navigation
jest.mock('next/navigation', () => ({
useRouter: () => ({
push: jest.fn(),
replace: jest.fn(),
refresh: jest.fn(),
back: jest.fn(),
forward: jest.fn(),
prefetch: jest.fn(),
}),
useSearchParams: () => new URLSearchParams(),
usePathname: () => '/',
}))
// Mock Response constructor for Next.js API routes
global.Response = class MockResponse {
constructor(body, options = {}) {
this.body = body
this.status = options.status || 200
this.statusText = options.statusText || 'OK'
this.headers = new Map([
['content-type', options.headers?.['content-type'] || 'application/json'],
...Object.entries(options.headers || {})
])
// Next.js Response.json() method
this.json = jest.fn(() => {
try {
return JSON.parse(body || '{}')
} catch {
return body
}
})
this.text = jest.fn(() => Promise.resolve(body || ''))
this.arrayBuffer = jest.fn(() => Promise.resolve(new ArrayBuffer(0)))
}
get(name) {
return this.headers.get(name.toLowerCase())
}
set(name, value) {
this.headers.set(name.toLowerCase(), value)
}
clone() {
return { ...this }
}
}
// Mock Request constructor for Next.js API routes
global.Request = class MockRequest {
constructor(url, options = {}) {
this.url = url
this.method = options.method || 'GET'
this.headers = new Map([
['content-type', 'application/json'],
...Object.entries(options.headers || {})
])
this.body = options.body || null
}
json() {
return Promise.resolve(this.body ? JSON.parse(this.body) : {})
}
text() {
return Promise.resolve(this.body || '')
}
get(name) {
return this.headers.get(name.toLowerCase())
}
}
// Mock NextResponse for API routes
global.NextResponse = {
json: jest.fn((data, options = {}) => {
return new global.Response(JSON.stringify(data), {
status: options.status || 200,
headers: {
'content-type': 'application/json',
...options.headers
}
})
}),
redirect: jest.fn((url, status = 302) => ({
url,
status,
headers: new Map([['location', url]])
})),
rewrite: jest.fn((url) => ({
url,
status: 200
}))
}
// Set up environment variables for tests
process.env.MONGODB_URI = 'mongodb://localhost:27017/test'
process.env.MONGODB_DB = 'test'
process.env.LOKI_HOST = 'http://loki:3100'
process.env.NODE_ENV = 'test'
process.env.NEXT_PUBLIC_CONTACT_EMAIL = 'info@mozdit.hu'
process.env.NEXT_PUBLIC_SITE_URL = 'https://localhost:3000'
process.env.npm_package_version = '1.0.0'
process.env.PACKAGE_VERSION = '1.0.0'
// Set up cleanup for consistent testing
afterEach(() => {
jest.clearAllMocks()
})
// Import React Testing Library DOM (using require for Jest setup files)
require('@testing-library/jest-dom')