@masonator/coolify-mcp 2.9.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -22
- package/dist/__tests__/coolify-client.test.js +656 -0
- package/dist/__tests__/mcp-server.test.js +215 -0
- package/dist/lib/coolify-client.d.ts +42 -1
- package/dist/lib/coolify-client.js +225 -0
- package/dist/lib/mcp-server.js +449 -20
- package/dist/types/coolify.d.ts +172 -0
- package/package.json +2 -2
|
@@ -969,6 +969,131 @@ describe('CoolifyClient', () => {
|
|
|
969
969
|
expect(callBody.custom_docker_run_options).toBe('--network=my-net');
|
|
970
970
|
expect(callBody.custom_labels).toBe('dHJhZWZpaw==');
|
|
971
971
|
});
|
|
972
|
+
// Regression for #178 — verify build-config and health_check_* fields reach
|
|
973
|
+
// the wire, not just zod-accepted then silently stripped by the hand-pick.
|
|
974
|
+
it('should pass build-config and health_check fields through createApplicationPublic', async () => {
|
|
975
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'new-app-uuid' }));
|
|
976
|
+
await client.createApplicationPublic({
|
|
977
|
+
project_uuid: 'proj-uuid',
|
|
978
|
+
server_uuid: 'server-uuid',
|
|
979
|
+
git_repository: 'https://github.com/user/monorepo',
|
|
980
|
+
git_branch: 'main',
|
|
981
|
+
build_pack: 'dockerfile',
|
|
982
|
+
ports_exposes: '3000',
|
|
983
|
+
base_directory: '/apps/api',
|
|
984
|
+
publish_directory: '/dist',
|
|
985
|
+
install_command: 'pnpm install',
|
|
986
|
+
build_command: 'pnpm build',
|
|
987
|
+
start_command: 'node dist/main.js',
|
|
988
|
+
dockerfile_location: '/apps/api/Dockerfile',
|
|
989
|
+
watch_paths: 'apps/api/**',
|
|
990
|
+
health_check_enabled: true,
|
|
991
|
+
health_check_path: '/health',
|
|
992
|
+
health_check_port: 3000,
|
|
993
|
+
health_check_start_period: 60,
|
|
994
|
+
});
|
|
995
|
+
const callBody = JSON.parse(mockFetch.mock.calls[0][1]?.body);
|
|
996
|
+
expect(callBody.base_directory).toBe('/apps/api');
|
|
997
|
+
expect(callBody.publish_directory).toBe('/dist');
|
|
998
|
+
expect(callBody.install_command).toBe('pnpm install');
|
|
999
|
+
expect(callBody.build_command).toBe('pnpm build');
|
|
1000
|
+
expect(callBody.start_command).toBe('node dist/main.js');
|
|
1001
|
+
expect(callBody.dockerfile_location).toBe('/apps/api/Dockerfile');
|
|
1002
|
+
expect(callBody.watch_paths).toBe('apps/api/**');
|
|
1003
|
+
expect(callBody.health_check_enabled).toBe(true);
|
|
1004
|
+
expect(callBody.health_check_path).toBe('/health');
|
|
1005
|
+
expect(callBody.health_check_port).toBe(3000);
|
|
1006
|
+
expect(callBody.health_check_start_period).toBe(60);
|
|
1007
|
+
});
|
|
1008
|
+
it('should pass build-config and health_check fields through createApplicationPrivateGH', async () => {
|
|
1009
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'new-app-uuid' }));
|
|
1010
|
+
await client.createApplicationPrivateGH({
|
|
1011
|
+
project_uuid: 'proj-uuid',
|
|
1012
|
+
server_uuid: 'server-uuid',
|
|
1013
|
+
github_app_uuid: 'gh-app-uuid',
|
|
1014
|
+
git_repository: 'org/monorepo',
|
|
1015
|
+
git_branch: 'main',
|
|
1016
|
+
base_directory: '/apps/api',
|
|
1017
|
+
dockerfile_location: '/apps/api/Dockerfile',
|
|
1018
|
+
watch_paths: 'apps/api/**',
|
|
1019
|
+
health_check_enabled: true,
|
|
1020
|
+
health_check_path: '/health',
|
|
1021
|
+
});
|
|
1022
|
+
const callBody = JSON.parse(mockFetch.mock.calls[0][1]?.body);
|
|
1023
|
+
expect(callBody.base_directory).toBe('/apps/api');
|
|
1024
|
+
expect(callBody.dockerfile_location).toBe('/apps/api/Dockerfile');
|
|
1025
|
+
expect(callBody.watch_paths).toBe('apps/api/**');
|
|
1026
|
+
expect(callBody.health_check_enabled).toBe(true);
|
|
1027
|
+
expect(callBody.health_check_path).toBe('/health');
|
|
1028
|
+
});
|
|
1029
|
+
it('should pass build-config and health_check fields through createApplicationPrivateKey', async () => {
|
|
1030
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'new-app-uuid' }));
|
|
1031
|
+
await client.createApplicationPrivateKey({
|
|
1032
|
+
project_uuid: 'proj-uuid',
|
|
1033
|
+
server_uuid: 'server-uuid',
|
|
1034
|
+
private_key_uuid: 'key-uuid',
|
|
1035
|
+
git_repository: 'git@github.com:org/monorepo.git',
|
|
1036
|
+
git_branch: 'main',
|
|
1037
|
+
base_directory: '/apps/api',
|
|
1038
|
+
publish_directory: '/dist',
|
|
1039
|
+
install_command: 'pnpm install',
|
|
1040
|
+
build_command: 'pnpm build',
|
|
1041
|
+
start_command: 'node dist/main.js',
|
|
1042
|
+
dockerfile_location: '/apps/api/Dockerfile',
|
|
1043
|
+
watch_paths: 'apps/api/**',
|
|
1044
|
+
health_check_enabled: true,
|
|
1045
|
+
health_check_path: '/health',
|
|
1046
|
+
health_check_port: 3000,
|
|
1047
|
+
});
|
|
1048
|
+
const callBody = JSON.parse(mockFetch.mock.calls[0][1]?.body);
|
|
1049
|
+
expect(callBody.base_directory).toBe('/apps/api');
|
|
1050
|
+
expect(callBody.dockerfile_location).toBe('/apps/api/Dockerfile');
|
|
1051
|
+
expect(callBody.watch_paths).toBe('apps/api/**');
|
|
1052
|
+
expect(callBody.health_check_path).toBe('/health');
|
|
1053
|
+
expect(callBody.health_check_port).toBe(3000);
|
|
1054
|
+
});
|
|
1055
|
+
it('should pass health_check fields through createApplicationDockerImage (build-config N/A)', async () => {
|
|
1056
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'new-app-uuid' }));
|
|
1057
|
+
await client.createApplicationDockerImage({
|
|
1058
|
+
project_uuid: 'proj-uuid',
|
|
1059
|
+
server_uuid: 'server-uuid',
|
|
1060
|
+
docker_registry_image_name: 'traefik/whoami',
|
|
1061
|
+
ports_exposes: '80',
|
|
1062
|
+
health_check_enabled: true,
|
|
1063
|
+
health_check_path: '/health',
|
|
1064
|
+
health_check_port: 80,
|
|
1065
|
+
health_check_method: 'GET',
|
|
1066
|
+
health_check_scheme: 'http',
|
|
1067
|
+
health_check_return_code: 200,
|
|
1068
|
+
health_check_interval: 30,
|
|
1069
|
+
health_check_timeout: 5,
|
|
1070
|
+
health_check_retries: 3,
|
|
1071
|
+
health_check_start_period: 60,
|
|
1072
|
+
});
|
|
1073
|
+
const callBody = JSON.parse(mockFetch.mock.calls[0][1]?.body);
|
|
1074
|
+
expect(callBody.health_check_enabled).toBe(true);
|
|
1075
|
+
expect(callBody.health_check_path).toBe('/health');
|
|
1076
|
+
expect(callBody.health_check_port).toBe(80);
|
|
1077
|
+
expect(callBody.health_check_method).toBe('GET');
|
|
1078
|
+
expect(callBody.health_check_scheme).toBe('http');
|
|
1079
|
+
expect(callBody.health_check_return_code).toBe(200);
|
|
1080
|
+
expect(callBody.health_check_interval).toBe(30);
|
|
1081
|
+
expect(callBody.health_check_timeout).toBe(5);
|
|
1082
|
+
expect(callBody.health_check_retries).toBe(3);
|
|
1083
|
+
expect(callBody.health_check_start_period).toBe(60);
|
|
1084
|
+
});
|
|
1085
|
+
it('should pass dockerfile_target_build through updateApplication (PATCH-only field)', async () => {
|
|
1086
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockApplication));
|
|
1087
|
+
await client.updateApplication('app-uuid', {
|
|
1088
|
+
dockerfile_location: '/apps/api/Dockerfile',
|
|
1089
|
+
dockerfile_target_build: 'production',
|
|
1090
|
+
base_directory: '/apps/api',
|
|
1091
|
+
});
|
|
1092
|
+
const callBody = JSON.parse(mockFetch.mock.calls[0][1]?.body);
|
|
1093
|
+
expect(callBody.dockerfile_location).toBe('/apps/api/Dockerfile');
|
|
1094
|
+
expect(callBody.dockerfile_target_build).toBe('production');
|
|
1095
|
+
expect(callBody.base_directory).toBe('/apps/api');
|
|
1096
|
+
});
|
|
972
1097
|
it('should pass destination_uuid through in createApplicationPublic', async () => {
|
|
973
1098
|
mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'new-app-uuid' }));
|
|
974
1099
|
await client.createApplicationPublic({
|
|
@@ -2914,4 +3039,535 @@ describe('CoolifyClient', () => {
|
|
|
2914
3039
|
});
|
|
2915
3040
|
});
|
|
2916
3041
|
});
|
|
3042
|
+
// ===========================================================================
|
|
3043
|
+
// Application Storage endpoints
|
|
3044
|
+
// ===========================================================================
|
|
3045
|
+
describe('listApplicationStorages', () => {
|
|
3046
|
+
it('should list application storages', async () => {
|
|
3047
|
+
const mockData = { persistent_storages: [], file_storages: [] };
|
|
3048
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockData));
|
|
3049
|
+
const result = await client.listApplicationStorages('app-uuid');
|
|
3050
|
+
expect(result).toEqual(mockData);
|
|
3051
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/applications/app-uuid/storages', expect.any(Object));
|
|
3052
|
+
});
|
|
3053
|
+
});
|
|
3054
|
+
describe('createApplicationStorage', () => {
|
|
3055
|
+
it('should create application storage', async () => {
|
|
3056
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Storage created.' }));
|
|
3057
|
+
const result = await client.createApplicationStorage('app-uuid', {
|
|
3058
|
+
type: 'persistent',
|
|
3059
|
+
mount_path: '/data',
|
|
3060
|
+
name: 'my-vol',
|
|
3061
|
+
});
|
|
3062
|
+
expect(result).toEqual({ message: 'Storage created.' });
|
|
3063
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/applications/app-uuid/storages', expect.objectContaining({ method: 'POST' }));
|
|
3064
|
+
});
|
|
3065
|
+
});
|
|
3066
|
+
describe('updateApplicationStorage', () => {
|
|
3067
|
+
it('should update application storage', async () => {
|
|
3068
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Storage updated.' }));
|
|
3069
|
+
const result = await client.updateApplicationStorage('app-uuid', {
|
|
3070
|
+
uuid: 'stor-uuid',
|
|
3071
|
+
type: 'persistent',
|
|
3072
|
+
name: 'renamed',
|
|
3073
|
+
});
|
|
3074
|
+
expect(result).toEqual({ message: 'Storage updated.' });
|
|
3075
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/applications/app-uuid/storages', expect.objectContaining({ method: 'PATCH' }));
|
|
3076
|
+
});
|
|
3077
|
+
});
|
|
3078
|
+
describe('deleteApplicationStorage', () => {
|
|
3079
|
+
it('should delete application storage', async () => {
|
|
3080
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Storage deleted.' }));
|
|
3081
|
+
const result = await client.deleteApplicationStorage('app-uuid', 'stor-uuid');
|
|
3082
|
+
expect(result).toEqual({ message: 'Storage deleted.' });
|
|
3083
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/applications/app-uuid/storages/stor-uuid', expect.objectContaining({ method: 'DELETE' }));
|
|
3084
|
+
});
|
|
3085
|
+
});
|
|
3086
|
+
// ===========================================================================
|
|
3087
|
+
// Application Scheduled Task endpoints
|
|
3088
|
+
// ===========================================================================
|
|
3089
|
+
describe('listApplicationScheduledTasks', () => {
|
|
3090
|
+
it('should list scheduled tasks', async () => {
|
|
3091
|
+
const mockTasks = [
|
|
3092
|
+
{
|
|
3093
|
+
id: 1,
|
|
3094
|
+
uuid: 'task-1',
|
|
3095
|
+
name: 'backup',
|
|
3096
|
+
command: 'pg_dump',
|
|
3097
|
+
frequency: '0 * * * *',
|
|
3098
|
+
enabled: true,
|
|
3099
|
+
timeout: 300,
|
|
3100
|
+
created_at: '2024-01-01',
|
|
3101
|
+
updated_at: '2024-01-01',
|
|
3102
|
+
},
|
|
3103
|
+
];
|
|
3104
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockTasks));
|
|
3105
|
+
const result = await client.listApplicationScheduledTasks('app-uuid');
|
|
3106
|
+
expect(result).toEqual(mockTasks);
|
|
3107
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/applications/app-uuid/scheduled-tasks', expect.any(Object));
|
|
3108
|
+
});
|
|
3109
|
+
});
|
|
3110
|
+
describe('createApplicationScheduledTask', () => {
|
|
3111
|
+
it('should create a scheduled task', async () => {
|
|
3112
|
+
const mockTask = {
|
|
3113
|
+
id: 1,
|
|
3114
|
+
uuid: 'task-1',
|
|
3115
|
+
name: 'backup',
|
|
3116
|
+
command: 'pg_dump',
|
|
3117
|
+
frequency: '0 * * * *',
|
|
3118
|
+
enabled: true,
|
|
3119
|
+
timeout: 300,
|
|
3120
|
+
created_at: '2024-01-01',
|
|
3121
|
+
updated_at: '2024-01-01',
|
|
3122
|
+
};
|
|
3123
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockTask));
|
|
3124
|
+
const result = await client.createApplicationScheduledTask('app-uuid', {
|
|
3125
|
+
name: 'backup',
|
|
3126
|
+
command: 'pg_dump',
|
|
3127
|
+
frequency: '0 * * * *',
|
|
3128
|
+
});
|
|
3129
|
+
expect(result).toEqual(mockTask);
|
|
3130
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/applications/app-uuid/scheduled-tasks', expect.objectContaining({ method: 'POST' }));
|
|
3131
|
+
});
|
|
3132
|
+
});
|
|
3133
|
+
describe('updateApplicationScheduledTask', () => {
|
|
3134
|
+
it('should update a scheduled task', async () => {
|
|
3135
|
+
const mockTask = {
|
|
3136
|
+
id: 1,
|
|
3137
|
+
uuid: 'task-1',
|
|
3138
|
+
name: 'backup-v2',
|
|
3139
|
+
command: 'pg_dump -Fc',
|
|
3140
|
+
frequency: '0 * * * *',
|
|
3141
|
+
enabled: true,
|
|
3142
|
+
timeout: 300,
|
|
3143
|
+
created_at: '2024-01-01',
|
|
3144
|
+
updated_at: '2024-01-01',
|
|
3145
|
+
};
|
|
3146
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockTask));
|
|
3147
|
+
const result = await client.updateApplicationScheduledTask('app-uuid', 'task-1', {
|
|
3148
|
+
name: 'backup-v2',
|
|
3149
|
+
});
|
|
3150
|
+
expect(result).toEqual(mockTask);
|
|
3151
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/applications/app-uuid/scheduled-tasks/task-1', expect.objectContaining({ method: 'PATCH' }));
|
|
3152
|
+
});
|
|
3153
|
+
});
|
|
3154
|
+
describe('deleteApplicationScheduledTask', () => {
|
|
3155
|
+
it('should delete a scheduled task', async () => {
|
|
3156
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Scheduled task deleted.' }));
|
|
3157
|
+
const result = await client.deleteApplicationScheduledTask('app-uuid', 'task-1');
|
|
3158
|
+
expect(result).toEqual({ message: 'Scheduled task deleted.' });
|
|
3159
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/applications/app-uuid/scheduled-tasks/task-1', expect.objectContaining({ method: 'DELETE' }));
|
|
3160
|
+
});
|
|
3161
|
+
});
|
|
3162
|
+
describe('listApplicationScheduledTaskExecutions', () => {
|
|
3163
|
+
it('should list task executions', async () => {
|
|
3164
|
+
const mockExecs = [
|
|
3165
|
+
{
|
|
3166
|
+
uuid: 'exec-1',
|
|
3167
|
+
status: 'success',
|
|
3168
|
+
retry_count: 0,
|
|
3169
|
+
created_at: '2024-01-01',
|
|
3170
|
+
updated_at: '2024-01-01',
|
|
3171
|
+
},
|
|
3172
|
+
];
|
|
3173
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockExecs));
|
|
3174
|
+
const result = await client.listApplicationScheduledTaskExecutions('app-uuid', 'task-1');
|
|
3175
|
+
expect(result).toEqual(mockExecs);
|
|
3176
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/applications/app-uuid/scheduled-tasks/task-1/executions', expect.any(Object));
|
|
3177
|
+
});
|
|
3178
|
+
});
|
|
3179
|
+
// ===========================================================================
|
|
3180
|
+
// Application Preview endpoint
|
|
3181
|
+
// ===========================================================================
|
|
3182
|
+
describe('deleteApplicationPreview', () => {
|
|
3183
|
+
it('should delete a preview deployment', async () => {
|
|
3184
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Preview deleted.' }));
|
|
3185
|
+
const result = await client.deleteApplicationPreview('app-uuid', 42);
|
|
3186
|
+
expect(result).toEqual({ message: 'Preview deleted.' });
|
|
3187
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/applications/app-uuid/previews/42', expect.objectContaining({ method: 'DELETE' }));
|
|
3188
|
+
});
|
|
3189
|
+
});
|
|
3190
|
+
// ===========================================================================
|
|
3191
|
+
// Database Environment Variable endpoints
|
|
3192
|
+
// ===========================================================================
|
|
3193
|
+
describe('listDatabaseEnvVars', () => {
|
|
3194
|
+
it('should list database env vars', async () => {
|
|
3195
|
+
const mockEnvs = [
|
|
3196
|
+
{
|
|
3197
|
+
id: 1,
|
|
3198
|
+
uuid: 'env-1',
|
|
3199
|
+
key: 'DB_HOST',
|
|
3200
|
+
value: 'localhost',
|
|
3201
|
+
is_build_time: false,
|
|
3202
|
+
is_literal: false,
|
|
3203
|
+
is_multiline: false,
|
|
3204
|
+
is_preview: false,
|
|
3205
|
+
is_shared: false,
|
|
3206
|
+
is_shown_once: false,
|
|
3207
|
+
created_at: '2024-01-01',
|
|
3208
|
+
updated_at: '2024-01-01',
|
|
3209
|
+
},
|
|
3210
|
+
];
|
|
3211
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockEnvs));
|
|
3212
|
+
const result = await client.listDatabaseEnvVars('db-uuid');
|
|
3213
|
+
expect(result).toEqual(mockEnvs);
|
|
3214
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/db-uuid/envs', expect.any(Object));
|
|
3215
|
+
});
|
|
3216
|
+
});
|
|
3217
|
+
describe('createDatabaseEnvVar', () => {
|
|
3218
|
+
it('should create a database env var', async () => {
|
|
3219
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'env-1' }));
|
|
3220
|
+
const result = await client.createDatabaseEnvVar('db-uuid', {
|
|
3221
|
+
key: 'DB_HOST',
|
|
3222
|
+
value: 'localhost',
|
|
3223
|
+
});
|
|
3224
|
+
expect(result).toEqual({ uuid: 'env-1' });
|
|
3225
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/db-uuid/envs', expect.objectContaining({ method: 'POST' }));
|
|
3226
|
+
});
|
|
3227
|
+
});
|
|
3228
|
+
describe('updateDatabaseEnvVar', () => {
|
|
3229
|
+
it('should update a database env var', async () => {
|
|
3230
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Updated.' }));
|
|
3231
|
+
const result = await client.updateDatabaseEnvVar('db-uuid', {
|
|
3232
|
+
key: 'DB_HOST',
|
|
3233
|
+
value: '127.0.0.1',
|
|
3234
|
+
});
|
|
3235
|
+
expect(result).toEqual({ message: 'Updated.' });
|
|
3236
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/db-uuid/envs', expect.objectContaining({ method: 'PATCH' }));
|
|
3237
|
+
});
|
|
3238
|
+
});
|
|
3239
|
+
describe('bulkUpdateDatabaseEnvVars', () => {
|
|
3240
|
+
it('should bulk update database env vars', async () => {
|
|
3241
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Bulk updated.' }));
|
|
3242
|
+
const result = await client.bulkUpdateDatabaseEnvVars('db-uuid', {
|
|
3243
|
+
data: [{ key: 'A', value: '1' }],
|
|
3244
|
+
});
|
|
3245
|
+
expect(result).toEqual({ message: 'Bulk updated.' });
|
|
3246
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/db-uuid/envs/bulk', expect.objectContaining({ method: 'PATCH' }));
|
|
3247
|
+
});
|
|
3248
|
+
});
|
|
3249
|
+
describe('deleteDatabaseEnvVar', () => {
|
|
3250
|
+
it('should delete a database env var', async () => {
|
|
3251
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Deleted.' }));
|
|
3252
|
+
const result = await client.deleteDatabaseEnvVar('db-uuid', 'env-uuid');
|
|
3253
|
+
expect(result).toEqual({ message: 'Deleted.' });
|
|
3254
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/db-uuid/envs/env-uuid', expect.objectContaining({ method: 'DELETE' }));
|
|
3255
|
+
});
|
|
3256
|
+
});
|
|
3257
|
+
// ===========================================================================
|
|
3258
|
+
// Database Storage endpoints
|
|
3259
|
+
// ===========================================================================
|
|
3260
|
+
describe('listDatabaseStorages', () => {
|
|
3261
|
+
it('should list database storages', async () => {
|
|
3262
|
+
const mockData = { persistent_storages: [], file_storages: [] };
|
|
3263
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockData));
|
|
3264
|
+
const result = await client.listDatabaseStorages('db-uuid');
|
|
3265
|
+
expect(result).toEqual(mockData);
|
|
3266
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/db-uuid/storages', expect.any(Object));
|
|
3267
|
+
});
|
|
3268
|
+
});
|
|
3269
|
+
describe('createDatabaseStorage', () => {
|
|
3270
|
+
it('should create database storage', async () => {
|
|
3271
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Created.' }));
|
|
3272
|
+
const result = await client.createDatabaseStorage('db-uuid', {
|
|
3273
|
+
type: 'persistent',
|
|
3274
|
+
mount_path: '/data',
|
|
3275
|
+
});
|
|
3276
|
+
expect(result).toEqual({ message: 'Created.' });
|
|
3277
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/db-uuid/storages', expect.objectContaining({ method: 'POST' }));
|
|
3278
|
+
});
|
|
3279
|
+
});
|
|
3280
|
+
describe('updateDatabaseStorage', () => {
|
|
3281
|
+
it('should update database storage', async () => {
|
|
3282
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Updated.' }));
|
|
3283
|
+
const result = await client.updateDatabaseStorage('db-uuid', {
|
|
3284
|
+
type: 'persistent',
|
|
3285
|
+
name: 'new-name',
|
|
3286
|
+
});
|
|
3287
|
+
expect(result).toEqual({ message: 'Updated.' });
|
|
3288
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/db-uuid/storages', expect.objectContaining({ method: 'PATCH' }));
|
|
3289
|
+
});
|
|
3290
|
+
});
|
|
3291
|
+
describe('deleteDatabaseStorage', () => {
|
|
3292
|
+
it('should delete database storage', async () => {
|
|
3293
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Deleted.' }));
|
|
3294
|
+
const result = await client.deleteDatabaseStorage('db-uuid', 'stor-uuid');
|
|
3295
|
+
expect(result).toEqual({ message: 'Deleted.' });
|
|
3296
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/db-uuid/storages/stor-uuid', expect.objectContaining({ method: 'DELETE' }));
|
|
3297
|
+
});
|
|
3298
|
+
});
|
|
3299
|
+
// ===========================================================================
|
|
3300
|
+
// Delete Backup Execution endpoint
|
|
3301
|
+
// ===========================================================================
|
|
3302
|
+
describe('deleteBackupExecution', () => {
|
|
3303
|
+
it('should delete a backup execution', async () => {
|
|
3304
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Deleted.' }));
|
|
3305
|
+
const result = await client.deleteBackupExecution('db-uuid', 'backup-uuid', 'exec-uuid');
|
|
3306
|
+
expect(result).toEqual({ message: 'Deleted.' });
|
|
3307
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/db-uuid/backups/backup-uuid/executions/exec-uuid', expect.objectContaining({ method: 'DELETE' }));
|
|
3308
|
+
});
|
|
3309
|
+
});
|
|
3310
|
+
// ===========================================================================
|
|
3311
|
+
// Service Environment Variable bulk endpoint
|
|
3312
|
+
// ===========================================================================
|
|
3313
|
+
describe('bulkUpdateServiceEnvVars', () => {
|
|
3314
|
+
it('should bulk update service env vars', async () => {
|
|
3315
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Bulk updated.' }));
|
|
3316
|
+
const result = await client.bulkUpdateServiceEnvVars('svc-uuid', {
|
|
3317
|
+
data: [{ key: 'A', value: '1' }],
|
|
3318
|
+
});
|
|
3319
|
+
expect(result).toEqual({ message: 'Bulk updated.' });
|
|
3320
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/svc-uuid/envs/bulk', expect.objectContaining({ method: 'PATCH' }));
|
|
3321
|
+
});
|
|
3322
|
+
});
|
|
3323
|
+
// ===========================================================================
|
|
3324
|
+
// Service Storage endpoints
|
|
3325
|
+
// ===========================================================================
|
|
3326
|
+
describe('listServiceStorages', () => {
|
|
3327
|
+
it('should list service storages', async () => {
|
|
3328
|
+
const mockData = { persistent_storages: [], file_storages: [] };
|
|
3329
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockData));
|
|
3330
|
+
const result = await client.listServiceStorages('svc-uuid');
|
|
3331
|
+
expect(result).toEqual(mockData);
|
|
3332
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/svc-uuid/storages', expect.any(Object));
|
|
3333
|
+
});
|
|
3334
|
+
});
|
|
3335
|
+
describe('createServiceStorage', () => {
|
|
3336
|
+
it('should create service storage', async () => {
|
|
3337
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Created.' }));
|
|
3338
|
+
const result = await client.createServiceStorage('svc-uuid', {
|
|
3339
|
+
type: 'file',
|
|
3340
|
+
mount_path: '/config',
|
|
3341
|
+
});
|
|
3342
|
+
expect(result).toEqual({ message: 'Created.' });
|
|
3343
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/svc-uuid/storages', expect.objectContaining({ method: 'POST' }));
|
|
3344
|
+
});
|
|
3345
|
+
});
|
|
3346
|
+
describe('updateServiceStorage', () => {
|
|
3347
|
+
it('should update service storage', async () => {
|
|
3348
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Updated.' }));
|
|
3349
|
+
const result = await client.updateServiceStorage('svc-uuid', {
|
|
3350
|
+
type: 'file',
|
|
3351
|
+
content: 'new content',
|
|
3352
|
+
});
|
|
3353
|
+
expect(result).toEqual({ message: 'Updated.' });
|
|
3354
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/svc-uuid/storages', expect.objectContaining({ method: 'PATCH' }));
|
|
3355
|
+
});
|
|
3356
|
+
});
|
|
3357
|
+
describe('deleteServiceStorage', () => {
|
|
3358
|
+
it('should delete service storage', async () => {
|
|
3359
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Deleted.' }));
|
|
3360
|
+
const result = await client.deleteServiceStorage('svc-uuid', 'stor-uuid');
|
|
3361
|
+
expect(result).toEqual({ message: 'Deleted.' });
|
|
3362
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/svc-uuid/storages/stor-uuid', expect.objectContaining({ method: 'DELETE' }));
|
|
3363
|
+
});
|
|
3364
|
+
});
|
|
3365
|
+
// ===========================================================================
|
|
3366
|
+
// Service Scheduled Task endpoints
|
|
3367
|
+
// ===========================================================================
|
|
3368
|
+
describe('listServiceScheduledTasks', () => {
|
|
3369
|
+
it('should list service scheduled tasks', async () => {
|
|
3370
|
+
mockFetch.mockResolvedValueOnce(mockResponse([]));
|
|
3371
|
+
const result = await client.listServiceScheduledTasks('svc-uuid');
|
|
3372
|
+
expect(result).toEqual([]);
|
|
3373
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/svc-uuid/scheduled-tasks', expect.any(Object));
|
|
3374
|
+
});
|
|
3375
|
+
});
|
|
3376
|
+
describe('createServiceScheduledTask', () => {
|
|
3377
|
+
it('should create a service scheduled task', async () => {
|
|
3378
|
+
const mockTask = {
|
|
3379
|
+
id: 1,
|
|
3380
|
+
uuid: 'task-1',
|
|
3381
|
+
name: 'cleanup',
|
|
3382
|
+
command: 'rm -rf /tmp/*',
|
|
3383
|
+
frequency: '0 0 * * *',
|
|
3384
|
+
enabled: true,
|
|
3385
|
+
timeout: 300,
|
|
3386
|
+
created_at: '2024-01-01',
|
|
3387
|
+
updated_at: '2024-01-01',
|
|
3388
|
+
};
|
|
3389
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockTask));
|
|
3390
|
+
const result = await client.createServiceScheduledTask('svc-uuid', {
|
|
3391
|
+
name: 'cleanup',
|
|
3392
|
+
command: 'rm -rf /tmp/*',
|
|
3393
|
+
frequency: '0 0 * * *',
|
|
3394
|
+
});
|
|
3395
|
+
expect(result).toEqual(mockTask);
|
|
3396
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/svc-uuid/scheduled-tasks', expect.objectContaining({ method: 'POST' }));
|
|
3397
|
+
});
|
|
3398
|
+
});
|
|
3399
|
+
describe('updateServiceScheduledTask', () => {
|
|
3400
|
+
it('should update a service scheduled task', async () => {
|
|
3401
|
+
const mockTask = {
|
|
3402
|
+
id: 1,
|
|
3403
|
+
uuid: 'task-1',
|
|
3404
|
+
name: 'cleanup-v2',
|
|
3405
|
+
command: 'rm -rf /tmp/*',
|
|
3406
|
+
frequency: '0 0 * * *',
|
|
3407
|
+
enabled: true,
|
|
3408
|
+
timeout: 600,
|
|
3409
|
+
created_at: '2024-01-01',
|
|
3410
|
+
updated_at: '2024-01-01',
|
|
3411
|
+
};
|
|
3412
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockTask));
|
|
3413
|
+
const result = await client.updateServiceScheduledTask('svc-uuid', 'task-1', {
|
|
3414
|
+
timeout: 600,
|
|
3415
|
+
});
|
|
3416
|
+
expect(result).toEqual(mockTask);
|
|
3417
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/svc-uuid/scheduled-tasks/task-1', expect.objectContaining({ method: 'PATCH' }));
|
|
3418
|
+
});
|
|
3419
|
+
});
|
|
3420
|
+
describe('deleteServiceScheduledTask', () => {
|
|
3421
|
+
it('should delete a service scheduled task', async () => {
|
|
3422
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Deleted.' }));
|
|
3423
|
+
const result = await client.deleteServiceScheduledTask('svc-uuid', 'task-1');
|
|
3424
|
+
expect(result).toEqual({ message: 'Deleted.' });
|
|
3425
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/svc-uuid/scheduled-tasks/task-1', expect.objectContaining({ method: 'DELETE' }));
|
|
3426
|
+
});
|
|
3427
|
+
});
|
|
3428
|
+
describe('listServiceScheduledTaskExecutions', () => {
|
|
3429
|
+
it('should list service task executions', async () => {
|
|
3430
|
+
mockFetch.mockResolvedValueOnce(mockResponse([]));
|
|
3431
|
+
const result = await client.listServiceScheduledTaskExecutions('svc-uuid', 'task-1');
|
|
3432
|
+
expect(result).toEqual([]);
|
|
3433
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/svc-uuid/scheduled-tasks/task-1/executions', expect.any(Object));
|
|
3434
|
+
});
|
|
3435
|
+
});
|
|
3436
|
+
// ===========================================================================
|
|
3437
|
+
// Hetzner Cloud endpoints
|
|
3438
|
+
// ===========================================================================
|
|
3439
|
+
describe('listHetznerLocations', () => {
|
|
3440
|
+
it('should list Hetzner locations', async () => {
|
|
3441
|
+
const mockLocs = [
|
|
3442
|
+
{
|
|
3443
|
+
id: 1,
|
|
3444
|
+
name: 'nbg1',
|
|
3445
|
+
description: 'Nuremberg',
|
|
3446
|
+
country: 'DE',
|
|
3447
|
+
city: 'Nuremberg',
|
|
3448
|
+
latitude: 49.45,
|
|
3449
|
+
longitude: 11.08,
|
|
3450
|
+
},
|
|
3451
|
+
];
|
|
3452
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockLocs));
|
|
3453
|
+
const result = await client.listHetznerLocations('token-uuid');
|
|
3454
|
+
expect(result).toEqual(mockLocs);
|
|
3455
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/hetzner/locations?cloud_provider_token_uuid=token-uuid', expect.any(Object));
|
|
3456
|
+
});
|
|
3457
|
+
});
|
|
3458
|
+
describe('listHetznerServerTypes', () => {
|
|
3459
|
+
it('should list Hetzner server types', async () => {
|
|
3460
|
+
mockFetch.mockResolvedValueOnce(mockResponse([]));
|
|
3461
|
+
const result = await client.listHetznerServerTypes('token-uuid');
|
|
3462
|
+
expect(result).toEqual([]);
|
|
3463
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/hetzner/server-types?cloud_provider_token_uuid=token-uuid', expect.any(Object));
|
|
3464
|
+
});
|
|
3465
|
+
});
|
|
3466
|
+
describe('listHetznerImages', () => {
|
|
3467
|
+
it('should list Hetzner images', async () => {
|
|
3468
|
+
mockFetch.mockResolvedValueOnce(mockResponse([]));
|
|
3469
|
+
const result = await client.listHetznerImages('token-uuid');
|
|
3470
|
+
expect(result).toEqual([]);
|
|
3471
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/hetzner/images?cloud_provider_token_uuid=token-uuid', expect.any(Object));
|
|
3472
|
+
});
|
|
3473
|
+
});
|
|
3474
|
+
describe('listHetznerSSHKeys', () => {
|
|
3475
|
+
it('should list Hetzner SSH keys', async () => {
|
|
3476
|
+
mockFetch.mockResolvedValueOnce(mockResponse([]));
|
|
3477
|
+
const result = await client.listHetznerSSHKeys('token-uuid');
|
|
3478
|
+
expect(result).toEqual([]);
|
|
3479
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/hetzner/ssh-keys?cloud_provider_token_uuid=token-uuid', expect.any(Object));
|
|
3480
|
+
});
|
|
3481
|
+
});
|
|
3482
|
+
describe('createHetznerServer', () => {
|
|
3483
|
+
it('should create a Hetzner server', async () => {
|
|
3484
|
+
const mockResp = { uuid: 'srv-uuid', hetzner_server_id: 12345, ip: '1.2.3.4' };
|
|
3485
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockResp));
|
|
3486
|
+
const result = await client.createHetznerServer({
|
|
3487
|
+
location: 'nbg1',
|
|
3488
|
+
server_type: 'cx11',
|
|
3489
|
+
image: 15512617,
|
|
3490
|
+
private_key_uuid: 'key-uuid',
|
|
3491
|
+
});
|
|
3492
|
+
expect(result).toEqual(mockResp);
|
|
3493
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/servers/hetzner', expect.objectContaining({ method: 'POST' }));
|
|
3494
|
+
});
|
|
3495
|
+
});
|
|
3496
|
+
// ===========================================================================
|
|
3497
|
+
// GitHub App Repository endpoints
|
|
3498
|
+
// ===========================================================================
|
|
3499
|
+
describe('listGitHubAppRepositories', () => {
|
|
3500
|
+
it('should list GitHub App repositories', async () => {
|
|
3501
|
+
const mockRepos = [
|
|
3502
|
+
{
|
|
3503
|
+
id: 1,
|
|
3504
|
+
name: 'my-repo',
|
|
3505
|
+
full_name: 'org/my-repo',
|
|
3506
|
+
private: true,
|
|
3507
|
+
html_url: 'https://github.com/org/my-repo',
|
|
3508
|
+
default_branch: 'main',
|
|
3509
|
+
},
|
|
3510
|
+
];
|
|
3511
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ repositories: mockRepos }));
|
|
3512
|
+
const result = await client.listGitHubAppRepositories(123);
|
|
3513
|
+
expect(result).toEqual(mockRepos);
|
|
3514
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/github-apps/123/repositories', expect.any(Object));
|
|
3515
|
+
});
|
|
3516
|
+
it('should return empty array when no repositories', async () => {
|
|
3517
|
+
mockFetch.mockResolvedValueOnce(mockResponse({}));
|
|
3518
|
+
const result = await client.listGitHubAppRepositories(123);
|
|
3519
|
+
expect(result).toEqual([]);
|
|
3520
|
+
});
|
|
3521
|
+
});
|
|
3522
|
+
describe('listGitHubAppBranches', () => {
|
|
3523
|
+
it('should list branches for a repo', async () => {
|
|
3524
|
+
const mockBranches = [{ name: 'main' }, { name: 'develop' }];
|
|
3525
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockBranches));
|
|
3526
|
+
const result = await client.listGitHubAppBranches(123, 'org', 'my-repo');
|
|
3527
|
+
expect(result).toEqual(mockBranches);
|
|
3528
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/github-apps/123/repositories/org/my-repo/branches', expect.any(Object));
|
|
3529
|
+
});
|
|
3530
|
+
});
|
|
3531
|
+
// ===========================================================================
|
|
3532
|
+
// Resources endpoint
|
|
3533
|
+
// ===========================================================================
|
|
3534
|
+
describe('listResources', () => {
|
|
3535
|
+
it('should list all resources', async () => {
|
|
3536
|
+
const mockData = [{ uuid: 'r1', type: 'application' }];
|
|
3537
|
+
mockFetch.mockResolvedValueOnce(mockResponse(mockData));
|
|
3538
|
+
const result = await client.listResources();
|
|
3539
|
+
expect(result).toEqual(mockData);
|
|
3540
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/resources', expect.any(Object));
|
|
3541
|
+
});
|
|
3542
|
+
});
|
|
3543
|
+
// ===========================================================================
|
|
3544
|
+
// Health endpoint
|
|
3545
|
+
// ===========================================================================
|
|
3546
|
+
describe('getHealth', () => {
|
|
3547
|
+
it('should check API health', async () => {
|
|
3548
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'OK' }));
|
|
3549
|
+
const result = await client.getHealth();
|
|
3550
|
+
expect(result).toEqual({ message: 'OK' });
|
|
3551
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/health', expect.any(Object));
|
|
3552
|
+
});
|
|
3553
|
+
});
|
|
3554
|
+
// ===========================================================================
|
|
3555
|
+
// API Enable/Disable endpoints
|
|
3556
|
+
// ===========================================================================
|
|
3557
|
+
describe('enableApi', () => {
|
|
3558
|
+
it('should enable the API', async () => {
|
|
3559
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'API enabled.' }));
|
|
3560
|
+
const result = await client.enableApi();
|
|
3561
|
+
expect(result).toEqual({ message: 'API enabled.' });
|
|
3562
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/enable', expect.objectContaining({ method: 'GET' }));
|
|
3563
|
+
});
|
|
3564
|
+
});
|
|
3565
|
+
describe('disableApi', () => {
|
|
3566
|
+
it('should disable the API', async () => {
|
|
3567
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'API disabled.' }));
|
|
3568
|
+
const result = await client.disableApi();
|
|
3569
|
+
expect(result).toEqual({ message: 'API disabled.' });
|
|
3570
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/disable', expect.objectContaining({ method: 'GET' }));
|
|
3571
|
+
});
|
|
3572
|
+
});
|
|
2917
3573
|
});
|