Skip to content

Commit 6e5f5d5

Browse files
committed
fix: setup automatic config "cleaner" for manually removed Leon instances
1 parent e1220f0 commit 6e5f5d5

5 files changed

Lines changed: 177 additions & 63 deletions

File tree

src/commands/__test__/info.test.ts

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { config } from '../../services/Config.js'
1212
import type { LeonInstanceOptions } from '../../services/LeonInstance.js'
1313
import { LeonInstance } from '../../services/LeonInstance.js'
1414
import { Log } from '../../services/Log.js'
15+
import { isExistingPath } from '../../utils/isExistingPath.js'
1516

1617
const leonInstanceOptions: LeonInstanceOptions = {
1718
name: 'random-name',
@@ -57,32 +58,56 @@ await tap.test('leon info', async (t) => {
5758
const exitCode = await command.execute()
5859
t.equal(exitCode, 0)
5960
t.equal(consoleLogSpy.calledWith(chalk.cyan('\nLeon instances:\n')), true)
61+
let infoResult = table([
62+
[chalk.bold('Name'), leonInstance.name],
63+
[chalk.bold('Path'), leonInstance.path],
64+
[chalk.bold('Mode'), leonInstance.mode],
65+
[chalk.bold('Birth date'), birthDayString],
66+
[chalk.bold('Version'), version]
67+
])
68+
infoResult += '\n------------------------------\n\n'
69+
t.equal(consoleLogSpy.calledWith(infoResult), true)
70+
}
71+
)
72+
73+
await t.test(
74+
'should succeeds and advise the user to create an instance',
75+
async (t) => {
76+
sinon.stub(console, 'log').value(() => {})
77+
const consoleLogSpy = sinon.spy(console, 'log')
78+
fsMock({
79+
[config.path]: JSON.stringify({ instances: [] })
80+
})
81+
const command = cli.process(['info'])
82+
const exitCode = await command.execute()
83+
t.equal(exitCode, 0)
84+
t.equal(
85+
consoleLogSpy.calledWith(chalk.bold('No Leon instances found.')),
86+
true
87+
)
6088
t.equal(
6189
consoleLogSpy.calledWith(
62-
table([
63-
[chalk.bold('Name'), leonInstance.name],
64-
[chalk.bold('Path'), leonInstance.path],
65-
[chalk.bold('Mode'), leonInstance.mode],
66-
[chalk.bold('Birth date'), birthDayString],
67-
[chalk.bold('Version'), version]
68-
])
90+
'You can give birth to a Leon instance using:'
6991
),
7092
true
7193
)
94+
t.equal(consoleLogSpy.calledWith(chalk.cyan('leon create birth')), true)
7295
}
7396
)
7497

7598
await t.test(
76-
'should succeeds and advise the user to create an instance',
99+
'should succeeds and advise the user to create an instance with instance path not found',
77100
async (t) => {
78101
sinon.stub(console, 'log').value(() => {})
79102
const consoleLogSpy = sinon.spy(console, 'log')
80103
fsMock({
81-
[config.path]: JSON.stringify({ instances: [] })
104+
[config.path]: JSON.stringify(configData)
82105
})
83106
const command = cli.process(['info'])
84107
const exitCode = await command.execute()
85108
t.equal(exitCode, 0)
109+
t.equal(await isExistingPath(leonInstance.path), false)
110+
t.strictSame(config.get('instances', []), [])
86111
t.equal(
87112
consoleLogSpy.calledWith(chalk.bold('No Leon instances found.')),
88113
true
@@ -110,18 +135,15 @@ await tap.test('leon info', async (t) => {
110135
const exitCode = await command.execute()
111136
t.equal(exitCode, 0)
112137
t.equal(consoleLogSpy.calledWith(chalk.cyan('\nLeon instances:\n')), true)
113-
t.equal(
114-
consoleLogSpy.calledWith(
115-
table([
116-
[chalk.bold('Name'), leonInstance.name],
117-
[chalk.bold('Path'), `${leonInstance.path}`],
118-
[chalk.bold('Mode'), leonInstance.mode],
119-
[chalk.bold('Birth date'), birthDayString],
120-
[chalk.bold('Version'), '0.0.0']
121-
])
122-
),
123-
true
124-
)
138+
let infoResult = table([
139+
[chalk.bold('Name'), leonInstance.name],
140+
[chalk.bold('Path'), leonInstance.path],
141+
[chalk.bold('Mode'), leonInstance.mode],
142+
[chalk.bold('Birth date'), birthDayString],
143+
[chalk.bold('Version'), '0.0.0']
144+
])
145+
infoResult += '\n------------------------------\n\n'
146+
t.equal(consoleLogSpy.calledWith(infoResult), true)
125147
}
126148
)
127149

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,81 @@
11
import tap from 'tap'
2+
import sinon from 'sinon'
3+
import fsMock from 'mock-fs'
4+
import chalk from 'chalk'
25

36
import { StartCommand } from '../start.js'
47
import { cli } from '../../cli.js'
8+
import type { ConfigData } from '../../services/Config.js'
9+
import { config } from '../../services/Config.js'
10+
import type { LeonInstanceOptions } from '../../services/LeonInstance.js'
11+
import { LeonInstance } from '../../services/LeonInstance.js'
12+
import { isExistingPath } from '../../utils/isExistingPath.js'
13+
14+
const leonInstanceOptions: LeonInstanceOptions = {
15+
name: 'random-name',
16+
birthDate: '2022-02-20T10:11:33.315Z',
17+
mode: 'docker',
18+
path: '/path',
19+
startCount: 0
20+
}
21+
22+
const leonInstance = new LeonInstance(leonInstanceOptions)
23+
const configData: ConfigData = {
24+
instances: [leonInstance]
25+
}
526

627
await tap.test('leon start', async (t) => {
28+
t.afterEach(() => {
29+
fsMock.restore()
30+
sinon.restore()
31+
})
32+
733
await t.test('should be instance of the command', async (t) => {
834
const command = cli.process(['start'])
935
t.equal(command instanceof StartCommand, true)
1036
})
37+
38+
await t.test(
39+
'should fails with instance not found (automatic config cleaner)',
40+
async (t) => {
41+
sinon.stub(console, 'error').value(() => {})
42+
const consoleErrorSpy = sinon.spy(console, 'error')
43+
fsMock({
44+
[config.path]: JSON.stringify(configData)
45+
})
46+
const command = cli.process(['start'])
47+
const exitCode = await command.execute()
48+
t.equal(exitCode, 1)
49+
t.equal(await isExistingPath(leonInstance.path), false)
50+
t.strictSame(config.get('instances', []), [])
51+
t.equal(
52+
consoleErrorSpy.calledWith(
53+
`${chalk.red('Error:')} You should have at least one instance.`
54+
),
55+
true
56+
)
57+
}
58+
)
59+
60+
await t.test(
61+
'should fails with instance not found with specified name',
62+
async (t) => {
63+
sinon.stub(console, 'error').value(() => {})
64+
const consoleErrorSpy = sinon.spy(console, 'error')
65+
fsMock({
66+
[config.path]: JSON.stringify(configData)
67+
})
68+
const command = cli.process(['start', '--name="random-name"'])
69+
const exitCode = await command.execute()
70+
t.equal(exitCode, 1)
71+
t.equal(
72+
consoleErrorSpy.calledWith(
73+
`${chalk.red(
74+
'Error:'
75+
)} This instance doesn't exists, please provider another name.`
76+
),
77+
true
78+
)
79+
}
80+
)
1181
})

src/commands/info.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,36 @@ export class InfoCommand extends Command {
1616
description: 'Name of the Leon instance.'
1717
})
1818

19+
public static logNoInstancesFound(): void {
20+
console.log(chalk.bold('No Leon instances found.'))
21+
console.log('You can give birth to a Leon instance using:')
22+
console.log(chalk.cyan('leon create birth'))
23+
}
24+
1925
async execute(): Promise<number> {
2026
try {
2127
if (this.name != null) {
2228
console.log()
2329
const leonInstance = await LeonInstance.get(this.name)
24-
await leonInstance.logInfo()
30+
console.log(await leonInstance.info())
2531
} else {
2632
const instances = config.get('instances', [])
2733
if (instances.length === 0) {
28-
console.log(chalk.bold('No Leon instances found.'))
29-
console.log('You can give birth to a Leon instance using:')
30-
console.log(chalk.cyan('leon create birth'))
34+
InfoCommand.logNoInstancesFound()
3135
} else {
32-
console.log(chalk.cyan('\nLeon instances:\n'))
36+
let infoResult = ''
3337
for (const instance of instances) {
34-
const leonInstance = new LeonInstance(instance)
35-
await leonInstance.logInfo()
36-
console.log('------------------------------\n')
38+
const leonInstance = await LeonInstance.find(instance.name)
39+
if (leonInstance != null) {
40+
infoResult += (await leonInstance.info()) + '\n'
41+
infoResult += '------------------------------\n\n'
42+
}
43+
}
44+
if (infoResult.length === 0) {
45+
InfoCommand.logNoInstancesFound()
46+
} else {
47+
console.log(chalk.cyan('\nLeon instances:\n'))
48+
console.log(infoResult)
3749
}
3850
}
3951
}

src/services/LeonInstance.ts

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export interface LeonInstanceOptions extends CreateOptions {
3838

3939
export class LeonInstance implements LeonInstanceOptions {
4040
static readonly INVALID_VERSION = '0.0.0'
41+
static readonly DEFAULT_START_PORT = 1337
4142

4243
public name: string
4344
public path: string
@@ -84,7 +85,8 @@ export class LeonInstance implements LeonInstanceOptions {
8485

8586
public async start(port?: number): Promise<void> {
8687
process.chdir(this.path)
87-
const LEON_PORT = port?.toString() ?? '1337'
88+
const LEON_PORT_STRING = port ?? LeonInstance.DEFAULT_START_PORT
89+
const LEON_PORT = LEON_PORT_STRING.toString()
8890
this.incrementStartCount()
8991
if (this.mode === 'docker') {
9092
return await this.startDocker(LEON_PORT)
@@ -160,24 +162,35 @@ export class LeonInstance implements LeonInstanceOptions {
160162
return instance.name === name
161163
})
162164
if (instance != null) {
163-
return new LeonInstance(instance)
165+
const leonInstance = new LeonInstance(instance)
166+
if (await isExistingPath(instance.path)) {
167+
return leonInstance
168+
}
169+
await leonInstance.delete()
170+
return null
164171
}
165172
return null
166173
}
167174

168175
static async get(name?: string): Promise<LeonInstance> {
176+
let instanceName = name ?? 'default'
177+
const logErrorAtLeastOneInstance = new LogError({
178+
message: 'You should have at least one instance.'
179+
})
169180
if (name == null) {
170181
const instances = config.get('instances', [])
171182
const isEmptyInstances = instances.length === 0
172183
if (isEmptyInstances) {
173-
throw new LogError({
174-
message: 'You should have at least one instance.'
175-
})
184+
throw logErrorAtLeastOneInstance
176185
}
177-
return new LeonInstance(instances[0])
186+
const firstInstance = instances[0]
187+
instanceName = firstInstance.name
178188
}
179-
const leonInstance = await LeonInstance.find(name)
189+
const leonInstance = await LeonInstance.find(instanceName)
180190
if (leonInstance == null) {
191+
if (name == null) {
192+
throw logErrorAtLeastOneInstance
193+
}
181194
throw new LogError({
182195
message: "This instance doesn't exists, please provider another name."
183196
})
@@ -243,7 +256,7 @@ export class LeonInstance implements LeonInstanceOptions {
243256
public async update(leon: Leon): Promise<void> {
244257
const currentVersion = await this.getVersion()
245258
const sourceCodePath = await leon.getSourceCode()
246-
const sourceCodeVersion = await LeonInstance.getVersion(sourceCodePath)
259+
const sourceCodeVersion = await this.getVersion()
247260
if (currentVersion !== sourceCodeVersion || leon.useDevelopGitBranch) {
248261
await fs.promises.rm(this.path, {
249262
force: true,
@@ -254,35 +267,29 @@ export class LeonInstance implements LeonInstanceOptions {
254267
}
255268
}
256269

257-
public static async getVersion(sourceCodePath: string): Promise<string> {
258-
const packageJsonPath = path.join(sourceCodePath, 'package.json')
270+
public async getVersion(): Promise<string> {
271+
const packageJsonPath = path.join(this.path, 'package.json')
259272
let version = LeonInstance.INVALID_VERSION
260273
if (await isExistingPath(packageJsonPath)) {
261274
const packageJSON = await readPackage({
262-
cwd: sourceCodePath,
275+
cwd: this.path,
263276
normalize: false
264277
})
265278
version = packageJSON.version ?? LeonInstance.INVALID_VERSION
266279
}
267280
return version
268281
}
269282

270-
public async getVersion(): Promise<string> {
271-
return await LeonInstance.getVersion(this.path)
272-
}
273-
274-
public async logInfo(): Promise<void> {
283+
public async info(): Promise<string> {
275284
const birthDay = new Date(this.birthDate)
276285
const birthDayString = date.format(birthDay, 'DD/MM/YYYY - HH:mm:ss')
277286
const version = await this.getVersion()
278-
console.log(
279-
table([
280-
[chalk.bold('Name'), this.name],
281-
[chalk.bold('Path'), this.path],
282-
[chalk.bold('Mode'), this.mode],
283-
[chalk.bold('Birth date'), birthDayString],
284-
[chalk.bold('Version'), version]
285-
])
286-
)
287+
return table([
288+
[chalk.bold('Name'), this.name],
289+
[chalk.bold('Path'), this.path],
290+
[chalk.bold('Mode'), this.mode],
291+
[chalk.bold('Birth date'), birthDayString],
292+
[chalk.bold('Version'), version]
293+
])
287294
}
288295
}

src/services/__test__/LeonInstance.test.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,24 @@ const configData: ConfigData = {
2121

2222
await tap.test('services/LeonInstance', async (t) => {
2323
await t.test('find', async (t) => {
24-
t.beforeEach(() => {
25-
fsMock({
26-
[config.path]: JSON.stringify(configData)
27-
})
28-
})
29-
3024
t.afterEach(() => {
3125
fsMock.restore()
3226
})
3327

3428
await t.test('should find the instance with its name', async (t) => {
29+
fsMock({
30+
[config.path]: JSON.stringify(configData),
31+
[leonInstance.path]: {}
32+
})
3533
const instance = await LeonInstance.find(leonInstance.name)
36-
t.not(instance, undefined)
3734
t.equal(instance?.name, leonInstance.name)
3835
})
3936

4037
await t.test('should not find the instance with wrong name', async (t) => {
38+
fsMock({
39+
[config.path]: JSON.stringify(configData),
40+
[leonInstance.path]: {}
41+
})
4142
const instance = await LeonInstance.find('wrong name')
4243
t.equal(instance, null)
4344
})
@@ -61,7 +62,8 @@ await tap.test('services/LeonInstance', async (t) => {
6162
'should return the first instance if name is undefined',
6263
async (t) => {
6364
fsMock({
64-
[config.path]: JSON.stringify(configData)
65+
[config.path]: JSON.stringify(configData),
66+
[leonInstance.path]: {}
6567
})
6668
const instance = await LeonInstance.get()
6769
t.equal(instance.name, leonInstance.name)
@@ -84,7 +86,8 @@ await tap.test('services/LeonInstance', async (t) => {
8486
'should return the instance with the name specified',
8587
async (t) => {
8688
fsMock({
87-
[config.path]: JSON.stringify(configData)
89+
[config.path]: JSON.stringify(configData),
90+
[leonInstance.path]: {}
8891
})
8992
const instance = await LeonInstance.get(leonInstance.name)
9093
t.equal(instance.name, leonInstance.name)

0 commit comments

Comments
 (0)