mirror of
https://github.com/ManInDark/HabitTrove.git
synced 2026-01-21 06:34:30 +01:00
feat: server permission checking and v0.2.28 release (#178)
This commit is contained in:
205
lib/startup-checks.test.ts
Normal file
205
lib/startup-checks.test.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
import { describe, expect, test, beforeEach, mock } from 'bun:test'
|
||||
import { checkStartupPermissions } from './startup-checks'
|
||||
|
||||
// Mock the fs promises module
|
||||
const mockStat = mock()
|
||||
const mockWriteFile = mock()
|
||||
const mockReadFile = mock()
|
||||
const mockUnlink = mock()
|
||||
|
||||
mock.module('fs', () => ({
|
||||
promises: {
|
||||
stat: mockStat,
|
||||
writeFile: mockWriteFile,
|
||||
readFile: mockReadFile,
|
||||
unlink: mockUnlink,
|
||||
},
|
||||
}))
|
||||
|
||||
describe('checkStartupPermissions', () => {
|
||||
beforeEach(() => {
|
||||
// Reset all mocks before each test
|
||||
mockStat.mockReset()
|
||||
mockWriteFile.mockReset()
|
||||
mockReadFile.mockReset()
|
||||
mockUnlink.mockReset()
|
||||
})
|
||||
|
||||
test('should return success when directory exists and has proper permissions', async () => {
|
||||
// Mock successful directory stat
|
||||
mockStat.mockResolvedValue({
|
||||
isDirectory: () => true,
|
||||
})
|
||||
|
||||
// Mock successful file operations
|
||||
mockWriteFile.mockResolvedValue(undefined)
|
||||
mockReadFile.mockResolvedValue('permission-test')
|
||||
mockUnlink.mockResolvedValue(undefined)
|
||||
|
||||
const result = await checkStartupPermissions()
|
||||
|
||||
expect(result).toEqual({ success: true })
|
||||
expect(mockStat).toHaveBeenCalledWith('data')
|
||||
expect(mockWriteFile).toHaveBeenCalledWith('data/.habittrove-permission-test', 'permission-test')
|
||||
expect(mockReadFile).toHaveBeenCalledWith('data/.habittrove-permission-test', 'utf8')
|
||||
expect(mockUnlink).toHaveBeenCalledWith('data/.habittrove-permission-test')
|
||||
})
|
||||
|
||||
test('should return error when directory does not exist', async () => {
|
||||
mockStat.mockRejectedValue(new Error('ENOENT: no such file or directory'))
|
||||
|
||||
const result = await checkStartupPermissions()
|
||||
|
||||
expect(result).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
path: 'data',
|
||||
message: 'Data directory \'data\' does not exist or is not accessible. Check volume mounts and permissions.',
|
||||
type: 'writable_data_dir'
|
||||
}
|
||||
})
|
||||
expect(mockStat).toHaveBeenCalledWith('data')
|
||||
expect(mockWriteFile).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('should return error when path exists but is not a directory', async () => {
|
||||
// Mock path exists but is a file, not directory
|
||||
mockStat.mockResolvedValue({
|
||||
isDirectory: () => false,
|
||||
})
|
||||
|
||||
const result = await checkStartupPermissions()
|
||||
|
||||
expect(result).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
path: 'data',
|
||||
message: 'Path \'data\' exists but is not a directory. Please ensure the data directory is properly configured.',
|
||||
type: 'writable_data_dir'
|
||||
}
|
||||
})
|
||||
expect(mockStat).toHaveBeenCalledWith('data')
|
||||
expect(mockWriteFile).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('should return error when write permission fails', async () => {
|
||||
// Mock successful directory stat
|
||||
mockStat.mockResolvedValue({
|
||||
isDirectory: () => true,
|
||||
})
|
||||
|
||||
// Mock write failure
|
||||
mockWriteFile.mockRejectedValue(new Error('EACCES: permission denied'))
|
||||
|
||||
const result = await checkStartupPermissions()
|
||||
|
||||
expect(result).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
path: 'data',
|
||||
message: 'Insufficient read/write permissions for data directory \'data\'. Check file permissions and ownership.',
|
||||
type: 'writable_data_dir'
|
||||
}
|
||||
})
|
||||
expect(mockStat).toHaveBeenCalledWith('data')
|
||||
expect(mockWriteFile).toHaveBeenCalledWith('data/.habittrove-permission-test', 'permission-test')
|
||||
expect(mockReadFile).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('should return error when read permission fails', async () => {
|
||||
// Mock successful directory stat and write
|
||||
mockStat.mockResolvedValue({
|
||||
isDirectory: () => true,
|
||||
})
|
||||
mockWriteFile.mockResolvedValue(undefined)
|
||||
|
||||
// Mock read failure
|
||||
mockReadFile.mockRejectedValue(new Error('EACCES: permission denied'))
|
||||
|
||||
const result = await checkStartupPermissions()
|
||||
|
||||
expect(result).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
path: 'data',
|
||||
message: 'Insufficient read/write permissions for data directory \'data\'. Check file permissions and ownership.',
|
||||
type: 'writable_data_dir'
|
||||
}
|
||||
})
|
||||
expect(mockStat).toHaveBeenCalledWith('data')
|
||||
expect(mockWriteFile).toHaveBeenCalledWith('data/.habittrove-permission-test', 'permission-test')
|
||||
expect(mockReadFile).toHaveBeenCalledWith('data/.habittrove-permission-test', 'utf8')
|
||||
})
|
||||
|
||||
test('should return error when read content does not match written content', async () => {
|
||||
// Mock successful directory stat and write
|
||||
mockStat.mockResolvedValue({
|
||||
isDirectory: () => true,
|
||||
})
|
||||
mockWriteFile.mockResolvedValue(undefined)
|
||||
|
||||
// Mock read with different content
|
||||
mockReadFile.mockResolvedValue('different-content')
|
||||
|
||||
const result = await checkStartupPermissions()
|
||||
|
||||
expect(result).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
path: 'data',
|
||||
message: 'Data integrity check failed in \'data\'. File system may be corrupted or have inconsistent behavior.',
|
||||
type: 'writable_data_dir'
|
||||
}
|
||||
})
|
||||
expect(mockStat).toHaveBeenCalledWith('data')
|
||||
expect(mockWriteFile).toHaveBeenCalledWith('data/.habittrove-permission-test', 'permission-test')
|
||||
expect(mockReadFile).toHaveBeenCalledWith('data/.habittrove-permission-test', 'utf8')
|
||||
expect(mockUnlink).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('should return error when cleanup (unlink) fails', async () => {
|
||||
// Mock successful directory stat, write, and read
|
||||
mockStat.mockResolvedValue({
|
||||
isDirectory: () => true,
|
||||
})
|
||||
mockWriteFile.mockResolvedValue(undefined)
|
||||
mockReadFile.mockResolvedValue('permission-test')
|
||||
|
||||
// Mock cleanup failure
|
||||
mockUnlink.mockRejectedValue(new Error('EACCES: permission denied'))
|
||||
|
||||
const result = await checkStartupPermissions()
|
||||
|
||||
// Should return error since cleanup failed and is part of the try-catch block
|
||||
expect(result).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
path: 'data',
|
||||
message: 'Insufficient read/write permissions for data directory \'data\'. Check file permissions and ownership.',
|
||||
type: 'writable_data_dir'
|
||||
}
|
||||
})
|
||||
expect(mockStat).toHaveBeenCalledWith('data')
|
||||
expect(mockWriteFile).toHaveBeenCalledWith('data/.habittrove-permission-test', 'permission-test')
|
||||
expect(mockReadFile).toHaveBeenCalledWith('data/.habittrove-permission-test', 'utf8')
|
||||
expect(mockUnlink).toHaveBeenCalledWith('data/.habittrove-permission-test')
|
||||
})
|
||||
|
||||
test('should use correct file paths', async () => {
|
||||
// Mock successful operations
|
||||
mockStat.mockResolvedValue({
|
||||
isDirectory: () => true,
|
||||
})
|
||||
mockWriteFile.mockResolvedValue(undefined)
|
||||
mockReadFile.mockResolvedValue('permission-test')
|
||||
mockUnlink.mockResolvedValue(undefined)
|
||||
|
||||
await checkStartupPermissions()
|
||||
|
||||
// Verify the correct paths are used
|
||||
expect(mockStat).toHaveBeenCalledWith('data')
|
||||
expect(mockWriteFile).toHaveBeenCalledWith('data/.habittrove-permission-test', 'permission-test')
|
||||
expect(mockReadFile).toHaveBeenCalledWith('data/.habittrove-permission-test', 'utf8')
|
||||
expect(mockUnlink).toHaveBeenCalledWith('data/.habittrove-permission-test')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user