mirror of
https://github.com/Cameri/nostream.git
synced 2025-04-08 20:08:07 +02:00
test: refactor settings
This commit is contained in:
parent
59bf1a67fd
commit
46cd022598
@ -2,13 +2,14 @@ import { Duplex, EventEmitter } from 'stream'
|
||||
import { IncomingMessage, Server, ServerResponse } from 'http'
|
||||
import packageJson from '../../package.json'
|
||||
|
||||
import { ISettings } from '../@types/settings'
|
||||
import { IWebServerAdapter } from '../@types/adapters'
|
||||
import { Settings } from '../utils/settings'
|
||||
|
||||
export class WebServerAdapter extends EventEmitter implements IWebServerAdapter {
|
||||
|
||||
public constructor(
|
||||
private readonly webServer: Server,
|
||||
private readonly settings: () => ISettings,
|
||||
) {
|
||||
super()
|
||||
this.webServer.on('request', this.onWebServerRequest.bind(this))
|
||||
@ -25,7 +26,7 @@ export class WebServerAdapter extends EventEmitter implements IWebServerAdapter
|
||||
if (request.method === 'GET' && request.headers['accept'] === 'application/nostr+json') {
|
||||
const {
|
||||
info: { name, description, pubkey, contact },
|
||||
} = Settings
|
||||
} = this.settings()
|
||||
|
||||
const relayInformationDocument = {
|
||||
name,
|
||||
|
@ -5,6 +5,7 @@ import { IWebSocketAdapter, IWebSocketServerAdapter } from '../@types/adapters'
|
||||
import { WebSocketAdapterEvent, WebSocketServerAdapterEvent } from '../constants/adapter'
|
||||
import { Event } from '../@types/event'
|
||||
import { Factory } from '../@types/base'
|
||||
import { ISettings } from '../@types/settings'
|
||||
import { propEq } from 'ramda'
|
||||
import { WebServerAdapter } from './web-server-adapter'
|
||||
|
||||
@ -22,9 +23,10 @@ export class WebSocketServerAdapter extends WebServerAdapter implements IWebSock
|
||||
private readonly createWebSocketAdapter: Factory<
|
||||
IWebSocketAdapter,
|
||||
[WebSocket, IncomingMessage, IWebSocketServerAdapter]
|
||||
>
|
||||
>,
|
||||
settings: () => ISettings,
|
||||
) {
|
||||
super(webServer)
|
||||
super(webServer, settings)
|
||||
|
||||
this.webSocketsAdapters = new WeakMap()
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { IncomingMessage, MessageType } from '../@types/messages'
|
||||
import { createSettings } from './settings-factory'
|
||||
import { DelegatedEventMessageHandler } from '../handlers/delegated-event-message-handler'
|
||||
import { delegatedEventStrategyFactory } from './delegated-event-strategy-factory'
|
||||
import { EventMessageHandler } from '../handlers/event-message-handler'
|
||||
@ -6,7 +7,6 @@ import { eventStrategyFactory } from './event-strategy-factory'
|
||||
import { IEventRepository } from '../@types/repositories'
|
||||
import { isDelegatedEvent } from '../utils/event'
|
||||
import { IWebSocketAdapter } from '../@types/adapters'
|
||||
import { Settings } from '../utils/settings'
|
||||
import { SubscribeMessageHandler } from '../handlers/subscribe-message-handler'
|
||||
import { UnsubscribeMessageHandler } from '../handlers/unsubscribe-message-handler'
|
||||
|
||||
@ -17,13 +17,17 @@ export const messageHandlerFactory = (
|
||||
case MessageType.EVENT:
|
||||
{
|
||||
if (isDelegatedEvent(message[1])) {
|
||||
return new DelegatedEventMessageHandler(adapter, delegatedEventStrategyFactory(eventRepository), Settings)
|
||||
return new DelegatedEventMessageHandler(
|
||||
adapter,
|
||||
delegatedEventStrategyFactory(eventRepository),
|
||||
createSettings
|
||||
)
|
||||
}
|
||||
|
||||
return new EventMessageHandler(adapter, eventStrategyFactory(eventRepository), Settings)
|
||||
return new EventMessageHandler(adapter, eventStrategyFactory(eventRepository), createSettings)
|
||||
}
|
||||
case MessageType.REQ:
|
||||
return new SubscribeMessageHandler(adapter, eventRepository)
|
||||
return new SubscribeMessageHandler(adapter, eventRepository, createSettings)
|
||||
case MessageType.CLOSE:
|
||||
return new UnsubscribeMessageHandler(adapter,)
|
||||
default:
|
||||
|
4
src/factories/settings-factory.ts
Normal file
4
src/factories/settings-factory.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { ISettings } from '../@types/settings'
|
||||
import { SettingsStatic } from '../utils/settings'
|
||||
|
||||
export const createSettings = (): ISettings => SettingsStatic.createSettings()
|
@ -13,7 +13,7 @@ export class EventMessageHandler implements IMessageHandler {
|
||||
public constructor(
|
||||
protected readonly webSocket: IWebSocketAdapter,
|
||||
protected readonly strategyFactory: Factory<IEventStrategy<Event, Promise<void>>, [Event, IWebSocketAdapter]>,
|
||||
private readonly settings: ISettings
|
||||
private readonly settings: () => ISettings
|
||||
) { }
|
||||
|
||||
public async handleMessage(message: IncomingEventMessage): Promise<void> {
|
||||
@ -49,7 +49,7 @@ export class EventMessageHandler implements IMessageHandler {
|
||||
|
||||
protected canAcceptEvent(event: Event): string | undefined {
|
||||
const now = Math.floor(Date.now()/1000)
|
||||
const limits = this.settings.limits.event
|
||||
const limits = this.settings().limits.event
|
||||
if (limits.createdAt.maxPositiveDelta > 0) {
|
||||
if (event.created_at > now + limits.createdAt.maxPositiveDelta) {
|
||||
return `created_at is more than ${limits.createdAt.maxPositiveDelta} seconds in the future`
|
||||
|
@ -8,8 +8,8 @@ import { streamEach, streamEnd, streamFilter, streamMap } from '../utils/stream'
|
||||
import { SubscriptionFilter, SubscriptionId } from '../@types/subscription'
|
||||
import { Event } from '../@types/event'
|
||||
import { IEventRepository } from '../@types/repositories'
|
||||
import { ISettings } from '../@types/settings'
|
||||
import { IWebSocketAdapter } from '../@types/adapters'
|
||||
import { Settings } from '../utils/settings'
|
||||
import { SubscribeMessage } from '../@types/messages'
|
||||
import { WebSocketAdapterEvent } from '../constants/adapter'
|
||||
|
||||
@ -19,6 +19,7 @@ export class SubscribeMessageHandler implements IMessageHandler, IAbortable {
|
||||
public constructor(
|
||||
private readonly webSocket: IWebSocketAdapter,
|
||||
private readonly eventRepository: IEventRepository,
|
||||
private readonly settings: () => ISettings,
|
||||
) {
|
||||
this.abortController = new AbortController()
|
||||
}
|
||||
@ -66,7 +67,7 @@ export class SubscribeMessageHandler implements IMessageHandler, IAbortable {
|
||||
}
|
||||
|
||||
private canSubscribe(subscriptionId: string, filters: SubscriptionFilter[]): string | undefined {
|
||||
const maxSubscriptions = Settings.limits.client.subscription.maxSubscriptions
|
||||
const maxSubscriptions = this.settings().limits.client.subscription.maxSubscriptions
|
||||
if (maxSubscriptions > 0) {
|
||||
const subscriptions = this.webSocket.getSubscriptions()
|
||||
if (!subscriptions.has(subscriptionId) && subscriptions.size + 1 > maxSubscriptions) {
|
||||
@ -74,7 +75,7 @@ export class SubscribeMessageHandler implements IMessageHandler, IAbortable {
|
||||
}
|
||||
}
|
||||
|
||||
const maxFilters = Settings.limits.client.subscription.maxFilters
|
||||
const maxFilters = this.settings().limits.client.subscription.maxFilters
|
||||
if (maxFilters > 0) {
|
||||
if (filters.length > maxFilters) {
|
||||
return `Too many filters: Number of filters per susbscription must be less or equal to ${maxFilters}`
|
||||
|
@ -4,6 +4,7 @@ import http from 'http'
|
||||
import process from 'process'
|
||||
import { WebSocketServer } from 'ws'
|
||||
|
||||
import { createSettings } from './factories/settings-factory'
|
||||
import { EventRepository } from './repositories/event-repository'
|
||||
import { getDbClient } from './database/client'
|
||||
import packageJson from '../package.json'
|
||||
@ -64,7 +65,8 @@ if (cluster.isPrimary) {
|
||||
const adapter = new WebSocketServerAdapter(
|
||||
server,
|
||||
wss,
|
||||
webSocketAdapterFactory(eventRepository)
|
||||
webSocketAdapterFactory(eventRepository),
|
||||
createSettings,
|
||||
)
|
||||
|
||||
adapter.listen(port)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
||||
import fs from 'fs'
|
||||
import { homedir } from 'os'
|
||||
import { join } from 'path'
|
||||
import { mergeDeepRight } from 'ramda'
|
||||
@ -6,85 +6,94 @@ import { mergeDeepRight } from 'ramda'
|
||||
import { ISettings } from '../@types/settings'
|
||||
import packageJson from '../../package.json'
|
||||
|
||||
export const getSettingsFilePath = (filename = 'settings.json'): string => join(
|
||||
process.env.NOSTR_CONFIG_DIR ?? join(homedir(), '.nostr'),
|
||||
filename,
|
||||
)
|
||||
export class SettingsStatic {
|
||||
static _settings: ISettings
|
||||
|
||||
let _settings: ISettings
|
||||
public static getSettingsFilePath(filename = 'settings.json') {
|
||||
return join(
|
||||
process.env.NOSTR_CONFIG_DIR ?? join(homedir(), '.nostr'),
|
||||
filename
|
||||
)
|
||||
}
|
||||
|
||||
export const getDefaultSettings = (): ISettings => ({
|
||||
info: {
|
||||
relay_url: `wss://${packageJson.name}.your-domain.com`,
|
||||
name: `${packageJson.name}.your-domain.com`,
|
||||
description: packageJson.description,
|
||||
pubkey: '',
|
||||
contact: 'operator@your-domain.com',
|
||||
},
|
||||
limits: {
|
||||
event: {
|
||||
eventId: {
|
||||
minLeadingZeroBits: 0,
|
||||
public static getDefaultSettings(): ISettings {
|
||||
return {
|
||||
info: {
|
||||
relay_url: 'wss://nostr-ts-relay.your-domain.com',
|
||||
name: `${packageJson.name}.your-domain.com`,
|
||||
description: packageJson.description,
|
||||
pubkey: 'replace-with-your-pubkey',
|
||||
contact: 'operator@your-domain.com',
|
||||
},
|
||||
kind: {
|
||||
whitelist: [],
|
||||
blacklist: [],
|
||||
limits: {
|
||||
event: {
|
||||
eventId: {
|
||||
minLeadingZeroBits: 0,
|
||||
},
|
||||
kind: {
|
||||
whitelist: [],
|
||||
blacklist: [],
|
||||
},
|
||||
pubkey: {
|
||||
minLeadingZeroBits: 0,
|
||||
whitelist: [],
|
||||
blacklist: [],
|
||||
},
|
||||
createdAt: {
|
||||
maxPositiveDelta: 900,
|
||||
maxNegativeDelta: 0, // disabled
|
||||
},
|
||||
},
|
||||
client: {
|
||||
subscription: {
|
||||
maxSubscriptions: 10,
|
||||
maxFilters: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
pubkey: {
|
||||
minLeadingZeroBits: 0,
|
||||
whitelist: [],
|
||||
blacklist: [],
|
||||
},
|
||||
createdAt: {
|
||||
maxPositiveDelta: 900, // +15 min
|
||||
maxNegativeDelta: 0, // disabled
|
||||
},
|
||||
},
|
||||
client: {
|
||||
subscription: {
|
||||
maxSubscriptions: 10,
|
||||
maxFilters: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const loadSettings = (path: string) => {
|
||||
return JSON.parse(
|
||||
readFileSync(
|
||||
public static loadSettings(path: string) {
|
||||
return JSON.parse(
|
||||
fs.readFileSync(
|
||||
path,
|
||||
{ encoding: 'utf-8' }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
public static createSettings(): ISettings {
|
||||
if (SettingsStatic._settings) {
|
||||
return SettingsStatic._settings
|
||||
}
|
||||
const path = SettingsStatic.getSettingsFilePath()
|
||||
const defaults = SettingsStatic.getDefaultSettings()
|
||||
try {
|
||||
|
||||
if (fs.existsSync(path)) {
|
||||
SettingsStatic._settings = mergeDeepRight(
|
||||
defaults,
|
||||
SettingsStatic.loadSettings(path)
|
||||
)
|
||||
} else {
|
||||
SettingsStatic.saveSettings(path, defaults)
|
||||
SettingsStatic._settings = mergeDeepRight({}, defaults)
|
||||
}
|
||||
|
||||
return SettingsStatic._settings
|
||||
} catch (error) {
|
||||
console.error('Unable to read config file. Reason: %s', error.message)
|
||||
|
||||
return defaults
|
||||
}
|
||||
}
|
||||
|
||||
public static saveSettings(path: string, settings: ISettings) {
|
||||
return fs.writeFileSync(
|
||||
path,
|
||||
{ encoding: 'utf-8' },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
const createSettings = (): ISettings => {
|
||||
const path = getSettingsFilePath()
|
||||
const defaults = getDefaultSettings()
|
||||
try {
|
||||
if (_settings) {
|
||||
return _settings
|
||||
}
|
||||
|
||||
if (!existsSync(path)) {
|
||||
saveSettings(path, defaults)
|
||||
}
|
||||
|
||||
_settings = mergeDeepRight(defaults, loadSettings(path))
|
||||
|
||||
return _settings
|
||||
} catch (error) {
|
||||
console.error('Unable to read config file. Reason: %s', error.message)
|
||||
|
||||
return defaults
|
||||
JSON.stringify(settings, null, 2),
|
||||
{ encoding: 'utf-8' }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const saveSettings = (path: string, settings: ISettings) => {
|
||||
return writeFileSync(
|
||||
path,
|
||||
JSON.stringify(settings, null, 2),
|
||||
{ encoding: 'utf-8' }
|
||||
)
|
||||
}
|
||||
export const Settings = createSettings()
|
||||
|
24
test/unit/factories/settings-factory.spec.ts
Normal file
24
test/unit/factories/settings-factory.spec.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { expect } from 'chai'
|
||||
import Sinon from 'sinon'
|
||||
|
||||
import { createSettings } from '../../../src/factories/settings-factory'
|
||||
import { SettingsStatic } from '../../../src/utils/settings'
|
||||
|
||||
describe('getSettings', () => {
|
||||
let createSettingsStub: Sinon.SinonStub
|
||||
|
||||
beforeEach(() => {
|
||||
createSettingsStub = Sinon.stub(SettingsStatic, 'createSettings')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
createSettingsStub.restore()
|
||||
})
|
||||
|
||||
it('calls createSettings and returns', () => {
|
||||
const settings = Symbol()
|
||||
createSettingsStub.returns(settings)
|
||||
|
||||
expect(createSettings()).to.equal(settings)
|
||||
})
|
||||
})
|
@ -64,7 +64,7 @@ describe('EventMessageHandler', () => {
|
||||
handler = new EventMessageHandler(
|
||||
webSocket as any,
|
||||
strategyFactoryStub,
|
||||
{} as any,
|
||||
() => ({}) as any,
|
||||
)
|
||||
})
|
||||
|
||||
@ -168,7 +168,7 @@ describe('EventMessageHandler', () => {
|
||||
handler = new EventMessageHandler(
|
||||
{} as any,
|
||||
() => null,
|
||||
settings,
|
||||
() => settings,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { expect } from 'chai'
|
||||
import fs from 'fs'
|
||||
import { homedir } from 'os'
|
||||
import { join } from 'path'
|
||||
import Sinon from 'sinon'
|
||||
|
||||
import { getDefaultSettings, getSettingsFilePath } from '../../../src/utils/settings'
|
||||
import { SettingsStatic } from '../../../src/utils/settings'
|
||||
|
||||
describe('Settings', () => {
|
||||
describe('getSettingsFilePath', () => {
|
||||
describe('SettingsStatic', () => {
|
||||
describe('.getSettingsFilePath', () => {
|
||||
let originalEnv: NodeJS.ProcessEnv
|
||||
|
||||
beforeEach(() => {
|
||||
@ -18,40 +20,39 @@ describe('Settings', () => {
|
||||
})
|
||||
|
||||
it('returns string ending with settings.json by default', () => {
|
||||
expect(getSettingsFilePath()).to.be.a('string').and.to.match(/settings\.json$/)
|
||||
expect(SettingsStatic.getSettingsFilePath()).to.be.a('string').and.to.match(/settings\.json$/)
|
||||
})
|
||||
|
||||
it('returns string ending with given string', () => {
|
||||
expect(getSettingsFilePath('ending')).to.be.a('string').and.to.match(/ending$/)
|
||||
expect(SettingsStatic.getSettingsFilePath('ending')).to.be.a('string').and.to.match(/ending$/)
|
||||
})
|
||||
|
||||
it('returns path begins with user\'s home dir by default', () => {
|
||||
expect(getSettingsFilePath()).to.be.a('string').and.equal(`${join(homedir(), '.nostr')}/settings.json`)
|
||||
expect(SettingsStatic.getSettingsFilePath()).to.be.a('string').and.equal(`${join(homedir(), '.nostr')}/settings.json`)
|
||||
})
|
||||
|
||||
it('returns path with NOSTR_CONFIG_DIR if set', () => {
|
||||
process.env.NOSTR_CONFIG_DIR = '/some/path'
|
||||
|
||||
expect(getSettingsFilePath()).to.be.a('string').and.equal('/some/path/settings.json')
|
||||
expect(SettingsStatic.getSettingsFilePath()).to.be.a('string').and.equal('/some/path/settings.json')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getDefaultSettings', () => {
|
||||
describe('.getDefaultSettings', () => {
|
||||
it('returns object with info', () => {
|
||||
expect(getDefaultSettings())
|
||||
expect(SettingsStatic.getDefaultSettings())
|
||||
.to.have.property('info')
|
||||
.and.to.deep.equal({
|
||||
relay_url: 'wss://nostr-ts-relay.your-domain.com',
|
||||
name: 'nostr-ts-relay.your-domain.com',
|
||||
description: 'A nostr relay written in Typescript.',
|
||||
pubkey: '',
|
||||
pubkey: 'replace-with-your-pubkey',
|
||||
contact: 'operator@your-domain.com',
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
it('returns object with default limits', () => {
|
||||
expect(getDefaultSettings())
|
||||
expect(SettingsStatic.getDefaultSettings())
|
||||
.to.have.property('limits')
|
||||
.and.to.deep.equal({
|
||||
event: {
|
||||
@ -82,15 +83,156 @@ describe('Settings', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// describe('loadSettings', () => {
|
||||
describe('.loadSettings', () => {
|
||||
let readFileSyncStub: Sinon.SinonStub
|
||||
|
||||
// })
|
||||
beforeEach(() => {
|
||||
readFileSyncStub = Sinon.stub(fs, 'readFileSync')
|
||||
})
|
||||
|
||||
// describe('createSettings', () => {
|
||||
afterEach(() => {
|
||||
readFileSyncStub.restore()
|
||||
})
|
||||
|
||||
// })
|
||||
it('loads settings from given path', () => {
|
||||
readFileSyncStub.returns('"content"')
|
||||
|
||||
// describe('saveSettings', () => {
|
||||
expect(SettingsStatic.loadSettings('/some/path')).to.equal('content')
|
||||
|
||||
// })
|
||||
})
|
||||
expect(readFileSyncStub).to.have.been.calledOnceWithExactly(
|
||||
'/some/path',
|
||||
{ encoding: 'utf-8' }
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('.createSettings', () => {
|
||||
let existsSyncStub: Sinon.SinonStub
|
||||
let getSettingsFilePathStub: Sinon.SinonStub
|
||||
let getDefaultSettingsStub: Sinon.SinonStub
|
||||
let saveSettingsStub: Sinon.SinonStub
|
||||
let loadSettingsStub: Sinon.SinonStub
|
||||
|
||||
let sandbox: Sinon.SinonSandbox
|
||||
|
||||
beforeEach(() => {
|
||||
SettingsStatic._settings = undefined
|
||||
|
||||
sandbox = Sinon.createSandbox()
|
||||
|
||||
existsSyncStub = sandbox.stub(fs, 'existsSync')
|
||||
getSettingsFilePathStub = sandbox.stub(SettingsStatic, 'getSettingsFilePath')
|
||||
getDefaultSettingsStub = sandbox.stub(SettingsStatic, 'getDefaultSettings')
|
||||
saveSettingsStub = sandbox.stub(SettingsStatic, 'saveSettings')
|
||||
loadSettingsStub = sandbox.stub(SettingsStatic, 'loadSettings')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('creates settings from default if settings file is missing', () => {
|
||||
getDefaultSettingsStub.returns({})
|
||||
getSettingsFilePathStub.returns('/some/path/settings.json')
|
||||
existsSyncStub.returns(false)
|
||||
|
||||
expect(SettingsStatic.createSettings()).to.deep.equal({})
|
||||
|
||||
expect(existsSyncStub).to.have.been.calledOnceWithExactly('/some/path/settings.json')
|
||||
expect(getSettingsFilePathStub).to.have.been.calledOnce
|
||||
expect(getDefaultSettingsStub).to.have.been.calledOnce
|
||||
expect(saveSettingsStub).to.have.been.calledOnceWithExactly(
|
||||
'/some/path/settings.json',
|
||||
{},
|
||||
)
|
||||
expect(loadSettingsStub).not.to.have.been.called
|
||||
})
|
||||
|
||||
it('returns default settings if saving settings file throws', () => {
|
||||
const error = new Error('mistakes were made')
|
||||
const defaults = Symbol()
|
||||
getSettingsFilePathStub.returns('/some/path/settings.json')
|
||||
getDefaultSettingsStub.returns(defaults)
|
||||
saveSettingsStub.throws(error)
|
||||
existsSyncStub.returns(false)
|
||||
|
||||
expect(SettingsStatic.createSettings()).to.equal(defaults)
|
||||
|
||||
expect(existsSyncStub).to.have.been.calledOnceWithExactly('/some/path/settings.json')
|
||||
expect(getSettingsFilePathStub).to.have.been.calledOnce
|
||||
expect(getDefaultSettingsStub).to.have.been.calledOnce
|
||||
expect(saveSettingsStub).to.have.been.calledOnceWithExactly(
|
||||
'/some/path/settings.json',
|
||||
defaults,
|
||||
)
|
||||
expect(loadSettingsStub).not.to.have.been.called
|
||||
})
|
||||
|
||||
it('loads settings from file if settings file is exists', () => {
|
||||
getDefaultSettingsStub.returns({})
|
||||
loadSettingsStub.returns({})
|
||||
getSettingsFilePathStub.returns('/some/path/settings.json')
|
||||
existsSyncStub.returns(true)
|
||||
|
||||
expect(SettingsStatic.createSettings()).to.deep.equal({})
|
||||
|
||||
expect(existsSyncStub).to.have.been.calledOnceWithExactly('/some/path/settings.json')
|
||||
expect(getSettingsFilePathStub).to.have.been.calledOnce
|
||||
expect(getDefaultSettingsStub).to.have.been.calledOnce
|
||||
expect(saveSettingsStub).not.to.have.been.called
|
||||
expect(loadSettingsStub).to.have.been.calledOnceWithExactly('/some/path/settings.json')
|
||||
})
|
||||
|
||||
it('returns defaults if loading settings file throws', () => {
|
||||
const defaults = Symbol()
|
||||
const error = new Error('mistakes were made')
|
||||
getDefaultSettingsStub.returns(defaults)
|
||||
loadSettingsStub.throws(error)
|
||||
getSettingsFilePathStub.returns('/some/path/settings.json')
|
||||
existsSyncStub.returns(true)
|
||||
|
||||
expect(SettingsStatic.createSettings()).to.equal(defaults)
|
||||
|
||||
expect(existsSyncStub).to.have.been.calledOnceWithExactly('/some/path/settings.json')
|
||||
expect(getSettingsFilePathStub).to.have.been.calledOnce
|
||||
expect(getDefaultSettingsStub).to.have.been.calledOnce
|
||||
expect(saveSettingsStub).not.to.have.been.called
|
||||
expect(loadSettingsStub).to.have.been.calledOnceWithExactly('/some/path/settings.json')
|
||||
})
|
||||
|
||||
it('returns cached settings if set', () => {
|
||||
const cachedSettings = Symbol()
|
||||
SettingsStatic._settings = cachedSettings as any
|
||||
|
||||
expect(SettingsStatic.createSettings()).to.equal(cachedSettings)
|
||||
|
||||
expect(getSettingsFilePathStub).not.to.have.been.calledOnce
|
||||
expect(getDefaultSettingsStub).not.to.have.been.calledOnce
|
||||
expect(existsSyncStub).not.to.have.been.called
|
||||
expect(saveSettingsStub).not.to.have.been.called
|
||||
expect(loadSettingsStub).not.to.have.been.called
|
||||
})
|
||||
})
|
||||
|
||||
describe('.saveSettings', () => {
|
||||
let writeFileSyncStub: Sinon.SinonStub
|
||||
|
||||
beforeEach(() => {
|
||||
writeFileSyncStub = Sinon.stub(fs, 'writeFileSync')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
writeFileSyncStub.restore()
|
||||
})
|
||||
|
||||
it('saves settings to given path', () => {
|
||||
SettingsStatic.saveSettings('/some/path/settings.json', {key: 'value'} as any)
|
||||
|
||||
expect(writeFileSyncStub).to.have.been.calledOnceWithExactly(
|
||||
'/some/path/settings.json',
|
||||
'{\n "key": "value"\n}',
|
||||
{ encoding: 'utf-8' }
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user