From db5089c06fe62396b8a992a27f84783bdb9a7766 Mon Sep 17 00:00:00 2001 From: 0div Date: Mon, 31 Mar 2025 19:22:13 -0700 Subject: [PATCH 01/22] * update envd spec & generate for js-sdk * update js-sdk file.list method to include optional depth param * update js-sdk tests --- .../src/envd/filesystem/filesystem_pb.ts | 7 +- packages/js-sdk/src/envd/schema.gen.ts | 380 +++++++++++------- .../js-sdk/src/sandbox/filesystem/index.ts | 10 +- .../js-sdk/tests/integration/next.test.ts | 20 + .../js-sdk/tests/sandbox/files/list.test.ts | 65 ++- spec/envd/filesystem/filesystem.proto | 1 + 6 files changed, 318 insertions(+), 165 deletions(-) create mode 100644 packages/js-sdk/tests/integration/next.test.ts diff --git a/packages/js-sdk/src/envd/filesystem/filesystem_pb.ts b/packages/js-sdk/src/envd/filesystem/filesystem_pb.ts index bf3763f369..5394b2fb8a 100644 --- a/packages/js-sdk/src/envd/filesystem/filesystem_pb.ts +++ b/packages/js-sdk/src/envd/filesystem/filesystem_pb.ts @@ -10,7 +10,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file filesystem/filesystem.proto. */ export const file_filesystem_filesystem: GenFile = /*@__PURE__*/ - fileDesc("ChtmaWxlc3lzdGVtL2ZpbGVzeXN0ZW0ucHJvdG8SCmZpbGVzeXN0ZW0iMgoLTW92ZVJlcXVlc3QSDgoGc291cmNlGAEgASgJEhMKC2Rlc3RpbmF0aW9uGAIgASgJIjQKDE1vdmVSZXNwb25zZRIkCgVlbnRyeRgBIAEoCzIVLmZpbGVzeXN0ZW0uRW50cnlJbmZvIh4KDk1ha2VEaXJSZXF1ZXN0EgwKBHBhdGgYASABKAkiNwoPTWFrZURpclJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8iHQoNUmVtb3ZlUmVxdWVzdBIMCgRwYXRoGAEgASgJIhAKDlJlbW92ZVJlc3BvbnNlIhsKC1N0YXRSZXF1ZXN0EgwKBHBhdGgYASABKAkiNAoMU3RhdFJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8iSwoJRW50cnlJbmZvEgwKBG5hbWUYASABKAkSIgoEdHlwZRgCIAEoDjIULmZpbGVzeXN0ZW0uRmlsZVR5cGUSDAoEcGF0aBgDIAEoCSIeCg5MaXN0RGlyUmVxdWVzdBIMCgRwYXRoGAEgASgJIjkKD0xpc3REaXJSZXNwb25zZRImCgdlbnRyaWVzGAEgAygLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8iMgoPV2F0Y2hEaXJSZXF1ZXN0EgwKBHBhdGgYASABKAkSEQoJcmVjdXJzaXZlGAIgASgIIkQKD0ZpbGVzeXN0ZW1FdmVudBIMCgRuYW1lGAEgASgJEiMKBHR5cGUYAiABKA4yFS5maWxlc3lzdGVtLkV2ZW50VHlwZSLgAQoQV2F0Y2hEaXJSZXNwb25zZRI4CgVzdGFydBgBIAEoCzInLmZpbGVzeXN0ZW0uV2F0Y2hEaXJSZXNwb25zZS5TdGFydEV2ZW50SAASMQoKZmlsZXN5c3RlbRgCIAEoCzIbLmZpbGVzeXN0ZW0uRmlsZXN5c3RlbUV2ZW50SAASOwoJa2VlcGFsaXZlGAMgASgLMiYuZmlsZXN5c3RlbS5XYXRjaERpclJlc3BvbnNlLktlZXBBbGl2ZUgAGgwKClN0YXJ0RXZlbnQaCwoJS2VlcEFsaXZlQgcKBWV2ZW50IjcKFENyZWF0ZVdhdGNoZXJSZXF1ZXN0EgwKBHBhdGgYASABKAkSEQoJcmVjdXJzaXZlGAIgASgIIisKFUNyZWF0ZVdhdGNoZXJSZXNwb25zZRISCgp3YXRjaGVyX2lkGAEgASgJIi0KF0dldFdhdGNoZXJFdmVudHNSZXF1ZXN0EhIKCndhdGNoZXJfaWQYASABKAkiRwoYR2V0V2F0Y2hlckV2ZW50c1Jlc3BvbnNlEisKBmV2ZW50cxgBIAMoCzIbLmZpbGVzeXN0ZW0uRmlsZXN5c3RlbUV2ZW50IioKFFJlbW92ZVdhdGNoZXJSZXF1ZXN0EhIKCndhdGNoZXJfaWQYASABKAkiFwoVUmVtb3ZlV2F0Y2hlclJlc3BvbnNlKlIKCEZpbGVUeXBlEhkKFUZJTEVfVFlQRV9VTlNQRUNJRklFRBAAEhIKDkZJTEVfVFlQRV9GSUxFEAESFwoTRklMRV9UWVBFX0RJUkVDVE9SWRACKpgBCglFdmVudFR5cGUSGgoWRVZFTlRfVFlQRV9VTlNQRUNJRklFRBAAEhUKEUVWRU5UX1RZUEVfQ1JFQVRFEAESFAoQRVZFTlRfVFlQRV9XUklURRACEhUKEUVWRU5UX1RZUEVfUkVNT1ZFEAMSFQoRRVZFTlRfVFlQRV9SRU5BTUUQBBIUChBFVkVOVF9UWVBFX0NITU9EEAUynwUKCkZpbGVzeXN0ZW0SOQoEU3RhdBIXLmZpbGVzeXN0ZW0uU3RhdFJlcXVlc3QaGC5maWxlc3lzdGVtLlN0YXRSZXNwb25zZRJCCgdNYWtlRGlyEhouZmlsZXN5c3RlbS5NYWtlRGlyUmVxdWVzdBobLmZpbGVzeXN0ZW0uTWFrZURpclJlc3BvbnNlEjkKBE1vdmUSFy5maWxlc3lzdGVtLk1vdmVSZXF1ZXN0GhguZmlsZXN5c3RlbS5Nb3ZlUmVzcG9uc2USQgoHTGlzdERpchIaLmZpbGVzeXN0ZW0uTGlzdERpclJlcXVlc3QaGy5maWxlc3lzdGVtLkxpc3REaXJSZXNwb25zZRI/CgZSZW1vdmUSGS5maWxlc3lzdGVtLlJlbW92ZVJlcXVlc3QaGi5maWxlc3lzdGVtLlJlbW92ZVJlc3BvbnNlEkcKCFdhdGNoRGlyEhsuZmlsZXN5c3RlbS5XYXRjaERpclJlcXVlc3QaHC5maWxlc3lzdGVtLldhdGNoRGlyUmVzcG9uc2UwARJUCg1DcmVhdGVXYXRjaGVyEiAuZmlsZXN5c3RlbS5DcmVhdGVXYXRjaGVyUmVxdWVzdBohLmZpbGVzeXN0ZW0uQ3JlYXRlV2F0Y2hlclJlc3BvbnNlEl0KEEdldFdhdGNoZXJFdmVudHMSIy5maWxlc3lzdGVtLkdldFdhdGNoZXJFdmVudHNSZXF1ZXN0GiQuZmlsZXN5c3RlbS5HZXRXYXRjaGVyRXZlbnRzUmVzcG9uc2USVAoNUmVtb3ZlV2F0Y2hlchIgLmZpbGVzeXN0ZW0uUmVtb3ZlV2F0Y2hlclJlcXVlc3QaIS5maWxlc3lzdGVtLlJlbW92ZVdhdGNoZXJSZXNwb25zZUJpCg5jb20uZmlsZXN5c3RlbUIPRmlsZXN5c3RlbVByb3RvUAGiAgNGWFiqAgpGaWxlc3lzdGVtygIKRmlsZXN5c3RlbeICFkZpbGVzeXN0ZW1cR1BCTWV0YWRhdGHqAgpGaWxlc3lzdGVtYgZwcm90bzM"); + fileDesc("ChtmaWxlc3lzdGVtL2ZpbGVzeXN0ZW0ucHJvdG8SCmZpbGVzeXN0ZW0iMgoLTW92ZVJlcXVlc3QSDgoGc291cmNlGAEgASgJEhMKC2Rlc3RpbmF0aW9uGAIgASgJIjQKDE1vdmVSZXNwb25zZRIkCgVlbnRyeRgBIAEoCzIVLmZpbGVzeXN0ZW0uRW50cnlJbmZvIh4KDk1ha2VEaXJSZXF1ZXN0EgwKBHBhdGgYASABKAkiNwoPTWFrZURpclJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8iHQoNUmVtb3ZlUmVxdWVzdBIMCgRwYXRoGAEgASgJIhAKDlJlbW92ZVJlc3BvbnNlIhsKC1N0YXRSZXF1ZXN0EgwKBHBhdGgYASABKAkiNAoMU3RhdFJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8iSwoJRW50cnlJbmZvEgwKBG5hbWUYASABKAkSIgoEdHlwZRgCIAEoDjIULmZpbGVzeXN0ZW0uRmlsZVR5cGUSDAoEcGF0aBgDIAEoCSItCg5MaXN0RGlyUmVxdWVzdBIMCgRwYXRoGAEgASgJEg0KBWRlcHRoGAIgASgNIjkKD0xpc3REaXJSZXNwb25zZRImCgdlbnRyaWVzGAEgAygLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8iMgoPV2F0Y2hEaXJSZXF1ZXN0EgwKBHBhdGgYASABKAkSEQoJcmVjdXJzaXZlGAIgASgIIkQKD0ZpbGVzeXN0ZW1FdmVudBIMCgRuYW1lGAEgASgJEiMKBHR5cGUYAiABKA4yFS5maWxlc3lzdGVtLkV2ZW50VHlwZSLgAQoQV2F0Y2hEaXJSZXNwb25zZRI4CgVzdGFydBgBIAEoCzInLmZpbGVzeXN0ZW0uV2F0Y2hEaXJSZXNwb25zZS5TdGFydEV2ZW50SAASMQoKZmlsZXN5c3RlbRgCIAEoCzIbLmZpbGVzeXN0ZW0uRmlsZXN5c3RlbUV2ZW50SAASOwoJa2VlcGFsaXZlGAMgASgLMiYuZmlsZXN5c3RlbS5XYXRjaERpclJlc3BvbnNlLktlZXBBbGl2ZUgAGgwKClN0YXJ0RXZlbnQaCwoJS2VlcEFsaXZlQgcKBWV2ZW50IjcKFENyZWF0ZVdhdGNoZXJSZXF1ZXN0EgwKBHBhdGgYASABKAkSEQoJcmVjdXJzaXZlGAIgASgIIisKFUNyZWF0ZVdhdGNoZXJSZXNwb25zZRISCgp3YXRjaGVyX2lkGAEgASgJIi0KF0dldFdhdGNoZXJFdmVudHNSZXF1ZXN0EhIKCndhdGNoZXJfaWQYASABKAkiRwoYR2V0V2F0Y2hlckV2ZW50c1Jlc3BvbnNlEisKBmV2ZW50cxgBIAMoCzIbLmZpbGVzeXN0ZW0uRmlsZXN5c3RlbUV2ZW50IioKFFJlbW92ZVdhdGNoZXJSZXF1ZXN0EhIKCndhdGNoZXJfaWQYASABKAkiFwoVUmVtb3ZlV2F0Y2hlclJlc3BvbnNlKlIKCEZpbGVUeXBlEhkKFUZJTEVfVFlQRV9VTlNQRUNJRklFRBAAEhIKDkZJTEVfVFlQRV9GSUxFEAESFwoTRklMRV9UWVBFX0RJUkVDVE9SWRACKpgBCglFdmVudFR5cGUSGgoWRVZFTlRfVFlQRV9VTlNQRUNJRklFRBAAEhUKEUVWRU5UX1RZUEVfQ1JFQVRFEAESFAoQRVZFTlRfVFlQRV9XUklURRACEhUKEUVWRU5UX1RZUEVfUkVNT1ZFEAMSFQoRRVZFTlRfVFlQRV9SRU5BTUUQBBIUChBFVkVOVF9UWVBFX0NITU9EEAUynwUKCkZpbGVzeXN0ZW0SOQoEU3RhdBIXLmZpbGVzeXN0ZW0uU3RhdFJlcXVlc3QaGC5maWxlc3lzdGVtLlN0YXRSZXNwb25zZRJCCgdNYWtlRGlyEhouZmlsZXN5c3RlbS5NYWtlRGlyUmVxdWVzdBobLmZpbGVzeXN0ZW0uTWFrZURpclJlc3BvbnNlEjkKBE1vdmUSFy5maWxlc3lzdGVtLk1vdmVSZXF1ZXN0GhguZmlsZXN5c3RlbS5Nb3ZlUmVzcG9uc2USQgoHTGlzdERpchIaLmZpbGVzeXN0ZW0uTGlzdERpclJlcXVlc3QaGy5maWxlc3lzdGVtLkxpc3REaXJSZXNwb25zZRI/CgZSZW1vdmUSGS5maWxlc3lzdGVtLlJlbW92ZVJlcXVlc3QaGi5maWxlc3lzdGVtLlJlbW92ZVJlc3BvbnNlEkcKCFdhdGNoRGlyEhsuZmlsZXN5c3RlbS5XYXRjaERpclJlcXVlc3QaHC5maWxlc3lzdGVtLldhdGNoRGlyUmVzcG9uc2UwARJUCg1DcmVhdGVXYXRjaGVyEiAuZmlsZXN5c3RlbS5DcmVhdGVXYXRjaGVyUmVxdWVzdBohLmZpbGVzeXN0ZW0uQ3JlYXRlV2F0Y2hlclJlc3BvbnNlEl0KEEdldFdhdGNoZXJFdmVudHMSIy5maWxlc3lzdGVtLkdldFdhdGNoZXJFdmVudHNSZXF1ZXN0GiQuZmlsZXN5c3RlbS5HZXRXYXRjaGVyRXZlbnRzUmVzcG9uc2USVAoNUmVtb3ZlV2F0Y2hlchIgLmZpbGVzeXN0ZW0uUmVtb3ZlV2F0Y2hlclJlcXVlc3QaIS5maWxlc3lzdGVtLlJlbW92ZVdhdGNoZXJSZXNwb25zZUJpCg5jb20uZmlsZXN5c3RlbUIPRmlsZXN5c3RlbVByb3RvUAGiAgNGWFiqAgpGaWxlc3lzdGVtygIKRmlsZXN5c3RlbeICFkZpbGVzeXN0ZW1cR1BCTWV0YWRhdGHqAgpGaWxlc3lzdGVtYgZwcm90bzM"); /** * @generated from message filesystem.MoveRequest @@ -184,6 +184,11 @@ export type ListDirRequest = Message<"filesystem.ListDirRequest"> & { * @generated from field: string path = 1; */ path: string; + + /** + * @generated from field: uint32 depth = 2; + */ + depth: number; }; /** diff --git a/packages/js-sdk/src/envd/schema.gen.ts b/packages/js-sdk/src/envd/schema.gen.ts index d415c0789d..8a256f5a60 100644 --- a/packages/js-sdk/src/envd/schema.gen.ts +++ b/packages/js-sdk/src/envd/schema.gen.ts @@ -3,166 +3,248 @@ * Do not make direct changes to the file. */ - export interface paths { - "/files": { - /** Download a file */ - get: { - parameters: { - query: { - path?: components["parameters"]["FilePath"]; - username: components["parameters"]["User"]; - }; - }; - responses: { - 200: components["responses"]["DownloadSuccess"]; - 400: components["responses"]["InvalidPath"]; - 401: components["responses"]["InvalidUser"]; - 404: components["responses"]["FileNotFound"]; - 500: components["responses"]["InternalServerError"]; - }; - }; - /** Upload a file and ensure the parent directories exist. If the file exists, it will be overwritten. */ - post: { - parameters: { - query: { - path?: components["parameters"]["FilePath"]; - username: components["parameters"]["User"]; - }; - }; - requestBody: components["requestBodies"]["File"]; - responses: { - 200: components["responses"]["UploadSuccess"]; - 400: components["responses"]["InvalidPath"]; - 401: components["responses"]["InvalidUser"]; - 500: components["responses"]["InternalServerError"]; - 507: components["responses"]["NotEnoughDiskSpace"]; - }; + "/files": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Download a file */ + get: { + parameters: { + query: { + /** @description Path to the file, URL encoded. Can be relative to user's home directory. */ + path?: components["parameters"]["FilePath"]; + /** @description User used for setting the owner, or resolving relative paths. */ + username: components["parameters"]["User"]; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: components["responses"]["DownloadSuccess"]; + 400: components["responses"]["InvalidPath"]; + 401: components["responses"]["InvalidUser"]; + 404: components["responses"]["FileNotFound"]; + 500: components["responses"]["InternalServerError"]; + }; + }; + put?: never; + /** Upload a file and ensure the parent directories exist. If the file exists, it will be overwritten. */ + post: { + parameters: { + query: { + /** @description Path to the file, URL encoded. Can be relative to user's home directory. */ + path?: components["parameters"]["FilePath"]; + /** @description User used for setting the owner, or resolving relative paths. */ + username: components["parameters"]["User"]; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: components["requestBodies"]["File"]; + responses: { + 200: components["responses"]["UploadSuccess"]; + 400: components["responses"]["InvalidPath"]; + 401: components["responses"]["InvalidUser"]; + 500: components["responses"]["InternalServerError"]; + 507: components["responses"]["NotEnoughDiskSpace"]; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - }; - "/health": { - /** Check the health of the service */ - get: { - responses: { - /** @description The service is healthy */ - 204: { - content: never; - }; - }; + "/health": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Check the health of the service */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The service is healthy */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - }; - "/init": { - /** Set env vars, ensure the time and metadata is synced with the host */ - post: { - requestBody?: { - content: { - "application/json": { - envVars?: components["schemas"]["EnvVars"]; - }; - }; - }; - responses: { - /** @description Env vars set, the time and metadata is synced with the host */ - 204: { - content: never; - }; - }; + "/init": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Set env vars, ensure the time and metadata is synced with the host */ + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + envVars?: components["schemas"]["EnvVars"]; + }; + }; + }; + responses: { + /** @description Env vars set, the time and metadata is synced with the host */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - }; } - export type webhooks = Record; - export interface components { - schemas: { - EntryInfo: { - /** @description Name of the file */ - name: string; - /** @description Path to the file */ - path: string; - /** - * @description Type of the file - * @enum {string} - */ - type: "file"; - }; - /** @description Environment variables to set */ - EnvVars: { - [key: string]: string; - }; - Error: { - /** @description Error code */ - code: number; - /** @description Error message */ - message: string; - }; - }; - responses: { - /** @description Entire file downloaded successfully. */ - DownloadSuccess: { - content: { - "application/octet-stream": string; - }; - }; - /** @description File not found */ - FileNotFound: { - content: { - "application/json": components["schemas"]["Error"]; - }; - }; - /** @description Internal server error */ - InternalServerError: { - content: { - "application/json": components["schemas"]["Error"]; - }; - }; - /** @description Invalid path */ - InvalidPath: { - content: { - "application/json": components["schemas"]["Error"]; - }; - }; - /** @description Invalid user */ - InvalidUser: { - content: { - "application/json": components["schemas"]["Error"]; - }; + schemas: { + EntryInfo: { + /** @description Name of the file */ + name: string; + /** @description Path to the file */ + path: string; + /** + * @description Type of the file + * @enum {string} + */ + type: "file"; + }; + /** @description Environment variables to set */ + EnvVars: { + [key: string]: string; + }; + Error: { + /** @description Error code */ + code: number; + /** @description Error message */ + message: string; + }; }; - /** @description Not enough disk space */ - NotEnoughDiskSpace: { - content: { - "application/json": components["schemas"]["Error"]; - }; + responses: { + /** @description Entire file downloaded successfully. */ + DownloadSuccess: { + headers: { + [name: string]: unknown; + }; + content: { + "application/octet-stream": string; + }; + }; + /** @description File not found */ + FileNotFound: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Internal server error */ + InternalServerError: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Invalid path */ + InvalidPath: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Invalid user */ + InvalidUser: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Not enough disk space */ + NotEnoughDiskSpace: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description The file was uploaded successfully. */ + UploadSuccess: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EntryInfo"][]; + }; + }; }; - /** @description The file was uploaded successfully. */ - UploadSuccess: { - content: { - "application/json": components["schemas"]["EntryInfo"][]; - }; + parameters: { + /** @description Path to the file, URL encoded. Can be relative to user's home directory. */ + FilePath: string; + /** @description User used for setting the owner, or resolving relative paths. */ + User: string; }; - }; - parameters: { - /** @description Path to the file, URL encoded. Can be relative to user's home directory. */ - FilePath?: string; - /** @description User used for setting the owner, or resolving relative paths. */ - User: string; - }; - requestBodies: { - File: { - content: { - "multipart/form-data": { - /** Format: binary */ - file?: string; - }; - }; + requestBodies: { + File: { + content: { + "multipart/form-data": { + /** Format: binary */ + file?: string; + }; + }; + }; }; - }; - headers: never; - pathItems: never; + headers: never; + pathItems: never; } - export type $defs = Record; - -export type external = Record; - export type operations = Record; diff --git a/packages/js-sdk/src/sandbox/filesystem/index.ts b/packages/js-sdk/src/sandbox/filesystem/index.ts index 13f511458b..5889077065 100644 --- a/packages/js-sdk/src/sandbox/filesystem/index.ts +++ b/packages/js-sdk/src/sandbox/filesystem/index.ts @@ -339,10 +339,16 @@ export class Filesystem { * * @returns list of entries in the sandbox filesystem directory. */ - async list(path: string, opts?: FilesystemRequestOpts): Promise { + async list( + path: string, + opts?: FilesystemRequestOpts & { depth?: number } + ): Promise { try { const res = await this.rpc.listDir( - { path }, + { + path, + depth: opts?.depth ?? 1, + }, { headers: authenticationHeader(opts?.user), signal: this.connectionConfig.getSignal(opts?.requestTimeoutMs), diff --git a/packages/js-sdk/tests/integration/next.test.ts b/packages/js-sdk/tests/integration/next.test.ts new file mode 100644 index 0000000000..e0a795b152 --- /dev/null +++ b/packages/js-sdk/tests/integration/next.test.ts @@ -0,0 +1,20 @@ +import { expect } from 'vitest' + +import { sandboxTest, isDebug, isIntegrationTest } from '../setup.js' +import { Sandbox } from '../../src' +import { wait } from '../setup.js' + +sandboxTest('kill existing sandbox', async ({ sandbox }) => { + const sbx = await Sandbox.create('base', { timeoutMs: 120_000 }) + + const handle = await sbx.commands.run('sleep 10', { background: true }) + await handle.kill() + + //expect(handle.stdout).toBe('') + //expect(handle.stderr).toBe('') + + const psHandle = await sbx.commands.run('ps aux', { background: true }) + console.log(psHandle.stdout) + + await wait(10_000) +}) diff --git a/packages/js-sdk/tests/sandbox/files/list.test.ts b/packages/js-sdk/tests/sandbox/files/list.test.ts index 448417f48a..eb1f21de3b 100644 --- a/packages/js-sdk/tests/sandbox/files/list.test.ts +++ b/packages/js-sdk/tests/sandbox/files/list.test.ts @@ -1,23 +1,62 @@ -import { assert } from 'vitest' +import { assert, onTestFinished } from 'vitest' import { sandboxTest } from '../../setup.js' sandboxTest('list directory', async ({ sandbox }) => { - const dirName = 'test_directory4' + const parentDirName = 'test_directory' - await sandbox.files.makeDir(dirName) + await sandbox.files.makeDir(parentDirName) + await sandbox.files.makeDir(`${parentDirName}/subdir1`) + await sandbox.files.makeDir(`${parentDirName}/subdir2`) + await sandbox.files.makeDir(`${parentDirName}/subdir1/subdir1_1`) + await sandbox.files.makeDir(`${parentDirName}/subdir1/subdir1_2`) + await sandbox.files.makeDir(`${parentDirName}/subdir2/subdir2_1`) + await sandbox.files.makeDir(`${parentDirName}/subdir2/subdir2_2`) - const files = await sandbox.files.list(dirName) - assert.equal(files.length, 0) + // explicit depth 0 (should default to 1) + const files0 = await sandbox.files.list(parentDirName, { depth: 0 }) + console.log(files0) + assert.equal(files0.length, 2) + assert.equal(files0[0].name, 'subdir1') + assert.equal(files0[1].name, 'subdir2') - await sandbox.files.write('test_directory4/test_file', 'test') + // default depth (1) + const files = await sandbox.files.list(parentDirName) + console.log(files) + assert.equal(files.length, 2) + assert.equal(files[0].name, 'subdir1') + assert.equal(files[1].name, 'subdir2') - const files1 = await sandbox.files.list(dirName) - assert.equal(files1.length, 1) - assert.equal(files1[0].name, 'test_file') - assert.equal(files1[0].type, 'file') - assert.equal(files1[0].path, `/home/user/${dirName}/test_file`) + // explicit depth 1 + const files1 = await sandbox.files.list(parentDirName, { depth: 1 }) + console.log(files1) + assert.equal(files1.length, 2) + assert.equal(files1[0].name, 'subdir1') + assert.equal(files1[1].name, 'subdir2') - const exists = await sandbox.files.exists(dirName) - assert.isTrue(exists) + // explicit depth 2 + const files2 = await sandbox.files.list(parentDirName, { depth: 2 }) + console.log(files2) + assert.equal(files2.length, 6) + assert.equal(files2[0].name, 'subdir1') + assert.equal(files2[1].name, 'subdir1_1') + assert.equal(files2[2].name, 'subdir1_2') + assert.equal(files2[3].name, 'subdir2') + assert.equal(files2[4].name, 'subdir2_1') + assert.equal(files2[5].name, 'subdir2_2') + + // explicit depth 3 (should be the same as depth 2) + const files3 = await sandbox.files.list(parentDirName, { depth: 3 }) + console.log(files3) + assert.equal(files3.length, 6) + assert.equal(files3[0].name, 'subdir1') + assert.equal(files3[1].name, 'subdir1_1') + assert.equal(files3[2].name, 'subdir1_2') + assert.equal(files3[3].name, 'subdir2') + assert.equal(files3[4].name, 'subdir2_1') + assert.equal(files3[5].name, 'subdir2_2') + + onTestFinished(() => { + sandbox.files.remove(parentDirName) + }) }) diff --git a/spec/envd/filesystem/filesystem.proto b/spec/envd/filesystem/filesystem.proto index 8c71c4f200..fc0976ac8a 100644 --- a/spec/envd/filesystem/filesystem.proto +++ b/spec/envd/filesystem/filesystem.proto @@ -62,6 +62,7 @@ enum FileType { message ListDirRequest { string path = 1; + uint32 depth = 2; } message ListDirResponse { From 8e9ea774257a9b3ce5dccb87c1e2ae7ab134ebdd Mon Sep 17 00:00:00 2001 From: 0div Date: Thu, 3 Apr 2025 17:03:29 -0700 Subject: [PATCH 02/22] * create Docker image with all the pinned deps needed to codegen * create codegen script using this container with mapped volumes * adapt Makefiles to this new approach * adapt where we install the connect-python protobuf binary for this to work --- Makefile | 8 +++- codegen.Dockerfile | 44 ++++++++++++++++++++ packages/connect-python/Makefile | 4 +- packages/python-sdk/scripts/fix-python-pb.sh | 8 ++-- scripts/codegen.sh | 8 ++++ spec/envd/buf-python.gen.yaml | 2 +- 6 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 codegen.Dockerfile create mode 100755 scripts/codegen.sh diff --git a/Makefile b/Makefile index af4b393022..7829df7ccc 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,10 @@ update-api-spec: @./scripts/update-api-spec.sh @echo "Done" +.PHONY: codegen +codegen: + @echo "Generating SDK code from openapi and envd spec" + @./scripts/codegen.sh generate: generate-js generate-python @@ -12,7 +16,9 @@ generate-js: cd spec/envd && buf generate --template buf-js.gen.yaml generate-python: - $(MAKE) -C packages/connect-python build + if [ ! -f "/go/bin/protoc-gen-connect-python" ]; then \ + $(MAKE) -C packages/connect-python build; \ + fi cd packages/python-sdk && make generate-api cd spec/envd && buf generate --template buf-python.gen.yaml cd packages/python-sdk && ./scripts/fix-python-pb.sh && black . diff --git a/codegen.Dockerfile b/codegen.Dockerfile new file mode 100644 index 0000000000..4f0d3e497f --- /dev/null +++ b/codegen.Dockerfile @@ -0,0 +1,44 @@ +FROM golang:1.23 + +# Install Golang deps +RUN go install github.com/bufbuild/buf/cmd/buf@v1.50.1 && \ + go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1 && \ + go install connectrpc.com/connect/cmd/protoc-gen-connect-go@v1.18.1 + +# Install our custom protoc plugin, connect-python +COPY ./packages/connect-python /packages/connect-python +RUN cd /packages/connect-python && make bin/protoc-gen-connect-python + + +FROM python:3.9 + +# Set working directory +WORKDIR /workspace + +ENV PROTOC_ZIP=protoc-29.3-linux-aarch_64.zip + +RUN curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v29.3/$PROTOC_ZIP +RUN unzip -o $PROTOC_ZIP -d /usr/local bin/protoc + +# Copy installed Go deps from previous build step +COPY --from=0 /go /go + +# Add Go binary to PATH +ENV PATH="/go/bin:${PATH}" + +# Install Python deps +RUN pip install black==23.7.0 pyyaml==6.0.2 openapi-python-client==0.24.3 + +# Install Node.js and npm +RUN apt-get update && \ + apt-get install -y curl && \ + curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \ + apt-get install -y nodejs && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Install Node.js deps +RUN npm install -g pnpm @connectrpc/protoc-gen-connect-es@1.6.1 @bufbuild/protoc-gen-es@2.2.2 + +# Generate when container starts +CMD ["make", "generate"] diff --git a/packages/connect-python/Makefile b/packages/connect-python/Makefile index 016c2f5c57..678288629a 100644 --- a/packages/connect-python/Makefile +++ b/packages/connect-python/Makefile @@ -21,9 +21,7 @@ upload: clean bin/$(plugin): $(wildcard cmd/$(plugin)/*.go) pyproject.toml Makefile - go build -o $@ \ - -ldflags "-w -s" \ - ./cmd/$(plugin) + go install -ldflags "-w -s" ./cmd/$(plugin) .PHONY: dev fmt lint upload clean build diff --git a/packages/python-sdk/scripts/fix-python-pb.sh b/packages/python-sdk/scripts/fix-python-pb.sh index a29b5855d8..7bdea385a3 100755 --- a/packages/python-sdk/scripts/fix-python-pb.sh +++ b/packages/python-sdk/scripts/fix-python-pb.sh @@ -4,8 +4,8 @@ rm -rf e2b/envd/__pycache__ rm -rf e2b/envd/filesystem/__pycache__ rm -rf e2b/envd/process/__pycache__ -sed -i '.bak' 's/from\ process\ import/from e2b.envd.process import/g' e2b/envd/process/* e2b/envd/filesystem/* -sed -i '.bak' 's/from\ filesystem\ import/from e2b.envd.filesystem import/g' e2b/envd/process/* e2b/envd/filesystem/* +sed -i.bak 's/from\ process\ import/from e2b.envd.process import/g' e2b/envd/process/* e2b/envd/filesystem/* +sed -i.bak 's/from\ filesystem\ import/from e2b.envd.filesystem import/g' e2b/envd/process/* e2b/envd/filesystem/* -rm e2b/envd/process/*.bak -rm e2b/envd/filesystem/*.bak +rm -f e2b/envd/process/*.bak +rm -f e2b/envd/filesystem/*.bak diff --git a/scripts/codegen.sh b/scripts/codegen.sh new file mode 100755 index 0000000000..738d19aaf0 --- /dev/null +++ b/scripts/codegen.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +set -eu + +# This script is used for codegen from openapi and envd spec in a docker container +# run this script from the root of the repo + +docker run -v "$(pwd):/workspace" $(docker build -q -t codegen-env . -f codegen.Dockerfile) diff --git a/spec/envd/buf-python.gen.yaml b/spec/envd/buf-python.gen.yaml index 6ee4cb61f4..286053db90 100644 --- a/spec/envd/buf-python.gen.yaml +++ b/spec/envd/buf-python.gen.yaml @@ -8,7 +8,7 @@ plugins: - pyi_out=../../packages/python-sdk/e2b/envd - name: connect-python out: ../../packages/python-sdk/e2b/envd - path: ../../packages/connect-python/bin/protoc-gen-connect-python + path: /go/bin/protoc-gen-connect-python managed: enabled: true From 5bc462232c0a5966e1b573dd0a35c3151215b839 Mon Sep 17 00:00:00 2001 From: 0div Date: Mon, 14 Apr 2025 11:49:48 -0700 Subject: [PATCH 03/22] add depth param to file list in python-sdk sync and async and update list tests --- .../sandbox_async/filesystem/filesystem.py | 4 +- .../e2b/sandbox_sync/filesystem/filesystem.py | 4 +- .../sandbox_async/files/test_files_list.py | 62 +++++++++++++++---- .../sandbox_sync/files/test_files_list.py | 56 +++++++++++++++++ .../sync/sandbox_sync/files/test_list_dir.py | 18 ------ 5 files changed, 112 insertions(+), 32 deletions(-) create mode 100644 packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py delete mode 100644 packages/python-sdk/tests/sync/sandbox_sync/files/test_list_dir.py diff --git a/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py b/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py index d1871bd704..a2789d247f 100644 --- a/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py +++ b/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py @@ -254,6 +254,7 @@ async def write( async def list( self, path: str, + depth: Optional[int] = 1, user: Username = "user", request_timeout: Optional[float] = None, ) -> List[EntryInfo]: @@ -261,6 +262,7 @@ async def list( List entries in a directory. :param path: Path to the directory + :param depth: Depth of the directory to list :param user: Run the operation as this user :param request_timeout: Timeout for the request in **seconds** @@ -268,7 +270,7 @@ async def list( """ try: res = await self._rpc.alist_dir( - filesystem_pb2.ListDirRequest(path=path), + filesystem_pb2.ListDirRequest(path=path, depth=depth), request_timeout=self._connection_config.get_request_timeout( request_timeout ), diff --git a/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py b/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py index 76c1ab4bf6..11c60a8e8c 100644 --- a/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py +++ b/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py @@ -250,6 +250,7 @@ def write( def list( self, path: str, + depth: Optional[int] = 1, user: Username = "user", request_timeout: Optional[float] = None, ) -> List[EntryInfo]: @@ -257,6 +258,7 @@ def list( List entries in a directory. :param path: Path to the directory + :param depth: Depth of the directory to list :param user: Run the operation as this user :param request_timeout: Timeout for the request in **seconds** @@ -264,7 +266,7 @@ def list( """ try: res = self._rpc.list_dir( - filesystem_pb2.ListDirRequest(path=path), + filesystem_pb2.ListDirRequest(path=path, depth=depth), request_timeout=self._connection_config.get_request_timeout( request_timeout ), diff --git a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py index 62113160f4..2c34bc855f 100644 --- a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py +++ b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py @@ -4,15 +4,53 @@ async def test_list_directory(async_sandbox: AsyncSandbox): - dir_name = f"test_directory_{uuid.uuid4()}" - - await async_sandbox.files.make_dir(dir_name) - files = await async_sandbox.files.list(dir_name) - assert len(files) == 0 - - await async_sandbox.files.write(f"{dir_name}/test_file", "test") - files1 = await async_sandbox.files.list(dir_name) - assert len(files1) == 1 - assert files1[0].name == "test_file" - assert files1[0].type == FileType.FILE - assert files1[0].path == f"/home/user/{dir_name}/test_file" + parent_dir_name = f"test_directory_{uuid.uuid4()}" + + await async_sandbox.files.make_dir(parent_dir_name) + await async_sandbox.files.make_dir(f"{parent_dir_name}/subdir1") + await async_sandbox.files.make_dir(f"{parent_dir_name}/subdir2") + await async_sandbox.files.make_dir(f"{parent_dir_name}/subdir1/subdir1_1") + await async_sandbox.files.make_dir(f"{parent_dir_name}/subdir1/subdir1_2") + await async_sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_1") + await async_sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_2") + + # explicit depth 0 (should default to 1) + files0 = await async_sandbox.files.list(parent_dir_name, depth=0) + assert len(files0) == 2 + assert files0[0].name == "subdir1" + assert files0[1].name == "subdir2" + + # default depth (1) + files = await async_sandbox.files.list(parent_dir_name) + assert len(files) == 2 + assert files[0].name == "subdir1" + assert files[1].name == "subdir2" + + # explicit depth 1 + files1 = await async_sandbox.files.list(parent_dir_name, depth=1) + assert len(files1) == 2 + assert files1[0].name == "subdir1" + assert files1[1].name == "subdir2" + + # explicit depth 2 + files2 = await async_sandbox.files.list(parent_dir_name, depth=2) + assert len(files2) == 6 + assert files2[0].name == "subdir1" + assert files2[1].name == "subdir1_1" + assert files2[2].name == "subdir1_2" + assert files2[3].name == "subdir2" + assert files2[4].name == "subdir2_1" + assert files2[5].name == "subdir2_2" + + # explicit depth 3 (should be the same as depth 2) + files3 = await async_sandbox.files.list(parent_dir_name, depth=3) + assert len(files3) == 6 + assert files3[0].name == "subdir1" + assert files3[1].name == "subdir1_1" + assert files3[2].name == "subdir1_2" + assert files3[3].name == "subdir2" + assert files3[4].name == "subdir2_1" + assert files3[5].name == "subdir2_2" + + # Cleanup + await async_sandbox.files.remove(parent_dir_name) diff --git a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py new file mode 100644 index 0000000000..d10cd1086d --- /dev/null +++ b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py @@ -0,0 +1,56 @@ +import uuid + +from e2b import Sandbox, FileType + + +def test_list_directory(sandbox: Sandbox): + parent_dir_name = f"test_directory_{uuid.uuid4()}" + + sandbox.files.make_dir(parent_dir_name) + sandbox.files.make_dir(f"{parent_dir_name}/subdir1") + sandbox.files.make_dir(f"{parent_dir_name}/subdir2") + sandbox.files.make_dir(f"{parent_dir_name}/subdir1/subdir1_1") + sandbox.files.make_dir(f"{parent_dir_name}/subdir1/subdir1_2") + sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_1") + sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_2") + + # explicit depth 0 (should default to 1) + files0 = sandbox.files.list(parent_dir_name, depth=0) + assert len(files0) == 2 + assert files0[0].name == "subdir1" + assert files0[1].name == "subdir2" + + # default depth (1) + files = sandbox.files.list(parent_dir_name) + assert len(files) == 2 + assert files[0].name == "subdir1" + assert files[1].name == "subdir2" + + # explicit depth 1 + files1 = sandbox.files.list(parent_dir_name, depth=1) + assert len(files1) == 2 + assert files1[0].name == "subdir1" + assert files1[1].name == "subdir2" + + # explicit depth 2 + files2 = sandbox.files.list(parent_dir_name, depth=2) + assert len(files2) == 6 + assert files2[0].name == "subdir1" + assert files2[1].name == "subdir1_1" + assert files2[2].name == "subdir1_2" + assert files2[3].name == "subdir2" + assert files2[4].name == "subdir2_1" + assert files2[5].name == "subdir2_2" + + # explicit depth 3 (should be the same as depth 2) + files3 = sandbox.files.list(parent_dir_name, depth=3) + assert len(files3) == 6 + assert files3[0].name == "subdir1" + assert files3[1].name == "subdir1_1" + assert files3[2].name == "subdir1_2" + assert files3[3].name == "subdir2" + assert files3[4].name == "subdir2_1" + assert files3[5].name == "subdir2_2" + + # Cleanup + sandbox.files.remove(parent_dir_name) diff --git a/packages/python-sdk/tests/sync/sandbox_sync/files/test_list_dir.py b/packages/python-sdk/tests/sync/sandbox_sync/files/test_list_dir.py deleted file mode 100644 index b243ce96a4..0000000000 --- a/packages/python-sdk/tests/sync/sandbox_sync/files/test_list_dir.py +++ /dev/null @@ -1,18 +0,0 @@ -import uuid - -from e2b import Sandbox, FileType - - -def test_list_directory(sandbox: Sandbox): - dir_name = f"test_directory_{uuid.uuid4()}" - - sandbox.files.make_dir(dir_name) - files = sandbox.files.list(dir_name) - assert len(files) == 0 - - sandbox.files.write(f"{dir_name}/test_file", "test") - files1 = sandbox.files.list(dir_name) - assert len(files1) == 1 - assert files1[0].name == "test_file" - assert files1[0].type == FileType.FILE - assert files1[0].path == f"/home/user/{dir_name}/test_file" From 14347a31cb4c9a444f252e98147653aad564a871 Mon Sep 17 00:00:00 2001 From: 0div Date: Mon, 14 Apr 2025 11:50:02 -0700 Subject: [PATCH 04/22] updated codegen --- .../e2b/envd/filesystem/filesystem_pb2.py | 64 +++++++++---------- .../e2b/envd/filesystem/filesystem_pb2.pyi | 8 ++- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.py b/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.py index b7e6c5f052..1a0da40fa4 100644 --- a/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.py +++ b/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.py @@ -19,7 +19,7 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x1b\x66ilesystem/filesystem.proto\x12\nfilesystem"G\n\x0bMoveRequest\x12\x16\n\x06source\x18\x01 \x01(\tR\x06source\x12 \n\x0b\x64\x65stination\x18\x02 \x01(\tR\x0b\x64\x65stination";\n\x0cMoveResponse\x12+\n\x05\x65ntry\x18\x01 \x01(\x0b\x32\x15.filesystem.EntryInfoR\x05\x65ntry"$\n\x0eMakeDirRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path">\n\x0fMakeDirResponse\x12+\n\x05\x65ntry\x18\x01 \x01(\x0b\x32\x15.filesystem.EntryInfoR\x05\x65ntry"#\n\rRemoveRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path"\x10\n\x0eRemoveResponse"!\n\x0bStatRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path";\n\x0cStatResponse\x12+\n\x05\x65ntry\x18\x01 \x01(\x0b\x32\x15.filesystem.EntryInfoR\x05\x65ntry"]\n\tEntryInfo\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12(\n\x04type\x18\x02 \x01(\x0e\x32\x14.filesystem.FileTypeR\x04type\x12\x12\n\x04path\x18\x03 \x01(\tR\x04path"$\n\x0eListDirRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path"B\n\x0fListDirResponse\x12/\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x15.filesystem.EntryInfoR\x07\x65ntries"C\n\x0fWatchDirRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\x12\x1c\n\trecursive\x18\x02 \x01(\x08R\trecursive"P\n\x0f\x46ilesystemEvent\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12)\n\x04type\x18\x02 \x01(\x0e\x32\x15.filesystem.EventTypeR\x04type"\xfe\x01\n\x10WatchDirResponse\x12?\n\x05start\x18\x01 \x01(\x0b\x32\'.filesystem.WatchDirResponse.StartEventH\x00R\x05start\x12=\n\nfilesystem\x18\x02 \x01(\x0b\x32\x1b.filesystem.FilesystemEventH\x00R\nfilesystem\x12\x46\n\tkeepalive\x18\x03 \x01(\x0b\x32&.filesystem.WatchDirResponse.KeepAliveH\x00R\tkeepalive\x1a\x0c\n\nStartEvent\x1a\x0b\n\tKeepAliveB\x07\n\x05\x65vent"H\n\x14\x43reateWatcherRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\x12\x1c\n\trecursive\x18\x02 \x01(\x08R\trecursive"6\n\x15\x43reateWatcherResponse\x12\x1d\n\nwatcher_id\x18\x01 \x01(\tR\twatcherId"8\n\x17GetWatcherEventsRequest\x12\x1d\n\nwatcher_id\x18\x01 \x01(\tR\twatcherId"O\n\x18GetWatcherEventsResponse\x12\x33\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x1b.filesystem.FilesystemEventR\x06\x65vents"5\n\x14RemoveWatcherRequest\x12\x1d\n\nwatcher_id\x18\x01 \x01(\tR\twatcherId"\x17\n\x15RemoveWatcherResponse*R\n\x08\x46ileType\x12\x19\n\x15\x46ILE_TYPE_UNSPECIFIED\x10\x00\x12\x12\n\x0e\x46ILE_TYPE_FILE\x10\x01\x12\x17\n\x13\x46ILE_TYPE_DIRECTORY\x10\x02*\x98\x01\n\tEventType\x12\x1a\n\x16\x45VENT_TYPE_UNSPECIFIED\x10\x00\x12\x15\n\x11\x45VENT_TYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENT_TYPE_WRITE\x10\x02\x12\x15\n\x11\x45VENT_TYPE_REMOVE\x10\x03\x12\x15\n\x11\x45VENT_TYPE_RENAME\x10\x04\x12\x14\n\x10\x45VENT_TYPE_CHMOD\x10\x05\x32\x9f\x05\n\nFilesystem\x12\x39\n\x04Stat\x12\x17.filesystem.StatRequest\x1a\x18.filesystem.StatResponse\x12\x42\n\x07MakeDir\x12\x1a.filesystem.MakeDirRequest\x1a\x1b.filesystem.MakeDirResponse\x12\x39\n\x04Move\x12\x17.filesystem.MoveRequest\x1a\x18.filesystem.MoveResponse\x12\x42\n\x07ListDir\x12\x1a.filesystem.ListDirRequest\x1a\x1b.filesystem.ListDirResponse\x12?\n\x06Remove\x12\x19.filesystem.RemoveRequest\x1a\x1a.filesystem.RemoveResponse\x12G\n\x08WatchDir\x12\x1b.filesystem.WatchDirRequest\x1a\x1c.filesystem.WatchDirResponse0\x01\x12T\n\rCreateWatcher\x12 .filesystem.CreateWatcherRequest\x1a!.filesystem.CreateWatcherResponse\x12]\n\x10GetWatcherEvents\x12#.filesystem.GetWatcherEventsRequest\x1a$.filesystem.GetWatcherEventsResponse\x12T\n\rRemoveWatcher\x12 .filesystem.RemoveWatcherRequest\x1a!.filesystem.RemoveWatcherResponseBi\n\x0e\x63om.filesystemB\x0f\x46ilesystemProtoP\x01\xa2\x02\x03\x46XX\xaa\x02\nFilesystem\xca\x02\nFilesystem\xe2\x02\x16\x46ilesystem\\GPBMetadata\xea\x02\nFilesystemb\x06proto3' + b'\n\x1b\x66ilesystem/filesystem.proto\x12\nfilesystem"G\n\x0bMoveRequest\x12\x16\n\x06source\x18\x01 \x01(\tR\x06source\x12 \n\x0b\x64\x65stination\x18\x02 \x01(\tR\x0b\x64\x65stination";\n\x0cMoveResponse\x12+\n\x05\x65ntry\x18\x01 \x01(\x0b\x32\x15.filesystem.EntryInfoR\x05\x65ntry"$\n\x0eMakeDirRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path">\n\x0fMakeDirResponse\x12+\n\x05\x65ntry\x18\x01 \x01(\x0b\x32\x15.filesystem.EntryInfoR\x05\x65ntry"#\n\rRemoveRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path"\x10\n\x0eRemoveResponse"!\n\x0bStatRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path";\n\x0cStatResponse\x12+\n\x05\x65ntry\x18\x01 \x01(\x0b\x32\x15.filesystem.EntryInfoR\x05\x65ntry"]\n\tEntryInfo\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12(\n\x04type\x18\x02 \x01(\x0e\x32\x14.filesystem.FileTypeR\x04type\x12\x12\n\x04path\x18\x03 \x01(\tR\x04path":\n\x0eListDirRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\x12\x14\n\x05\x64\x65pth\x18\x02 \x01(\rR\x05\x64\x65pth"B\n\x0fListDirResponse\x12/\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x15.filesystem.EntryInfoR\x07\x65ntries"C\n\x0fWatchDirRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\x12\x1c\n\trecursive\x18\x02 \x01(\x08R\trecursive"P\n\x0f\x46ilesystemEvent\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12)\n\x04type\x18\x02 \x01(\x0e\x32\x15.filesystem.EventTypeR\x04type"\xfe\x01\n\x10WatchDirResponse\x12?\n\x05start\x18\x01 \x01(\x0b\x32\'.filesystem.WatchDirResponse.StartEventH\x00R\x05start\x12=\n\nfilesystem\x18\x02 \x01(\x0b\x32\x1b.filesystem.FilesystemEventH\x00R\nfilesystem\x12\x46\n\tkeepalive\x18\x03 \x01(\x0b\x32&.filesystem.WatchDirResponse.KeepAliveH\x00R\tkeepalive\x1a\x0c\n\nStartEvent\x1a\x0b\n\tKeepAliveB\x07\n\x05\x65vent"H\n\x14\x43reateWatcherRequest\x12\x12\n\x04path\x18\x01 \x01(\tR\x04path\x12\x1c\n\trecursive\x18\x02 \x01(\x08R\trecursive"6\n\x15\x43reateWatcherResponse\x12\x1d\n\nwatcher_id\x18\x01 \x01(\tR\twatcherId"8\n\x17GetWatcherEventsRequest\x12\x1d\n\nwatcher_id\x18\x01 \x01(\tR\twatcherId"O\n\x18GetWatcherEventsResponse\x12\x33\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x1b.filesystem.FilesystemEventR\x06\x65vents"5\n\x14RemoveWatcherRequest\x12\x1d\n\nwatcher_id\x18\x01 \x01(\tR\twatcherId"\x17\n\x15RemoveWatcherResponse*R\n\x08\x46ileType\x12\x19\n\x15\x46ILE_TYPE_UNSPECIFIED\x10\x00\x12\x12\n\x0e\x46ILE_TYPE_FILE\x10\x01\x12\x17\n\x13\x46ILE_TYPE_DIRECTORY\x10\x02*\x98\x01\n\tEventType\x12\x1a\n\x16\x45VENT_TYPE_UNSPECIFIED\x10\x00\x12\x15\n\x11\x45VENT_TYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENT_TYPE_WRITE\x10\x02\x12\x15\n\x11\x45VENT_TYPE_REMOVE\x10\x03\x12\x15\n\x11\x45VENT_TYPE_RENAME\x10\x04\x12\x14\n\x10\x45VENT_TYPE_CHMOD\x10\x05\x32\x9f\x05\n\nFilesystem\x12\x39\n\x04Stat\x12\x17.filesystem.StatRequest\x1a\x18.filesystem.StatResponse\x12\x42\n\x07MakeDir\x12\x1a.filesystem.MakeDirRequest\x1a\x1b.filesystem.MakeDirResponse\x12\x39\n\x04Move\x12\x17.filesystem.MoveRequest\x1a\x18.filesystem.MoveResponse\x12\x42\n\x07ListDir\x12\x1a.filesystem.ListDirRequest\x1a\x1b.filesystem.ListDirResponse\x12?\n\x06Remove\x12\x19.filesystem.RemoveRequest\x1a\x1a.filesystem.RemoveResponse\x12G\n\x08WatchDir\x12\x1b.filesystem.WatchDirRequest\x1a\x1c.filesystem.WatchDirResponse0\x01\x12T\n\rCreateWatcher\x12 .filesystem.CreateWatcherRequest\x1a!.filesystem.CreateWatcherResponse\x12]\n\x10GetWatcherEvents\x12#.filesystem.GetWatcherEventsRequest\x1a$.filesystem.GetWatcherEventsResponse\x12T\n\rRemoveWatcher\x12 .filesystem.RemoveWatcherRequest\x1a!.filesystem.RemoveWatcherResponseBi\n\x0e\x63om.filesystemB\x0f\x46ilesystemProtoP\x01\xa2\x02\x03\x46XX\xaa\x02\nFilesystem\xca\x02\nFilesystem\xe2\x02\x16\x46ilesystem\\GPBMetadata\xea\x02\nFilesystemb\x06proto3' ) _globals = globals() @@ -32,10 +32,10 @@ _globals[ "DESCRIPTOR" ]._serialized_options = b"\n\016com.filesystemB\017FilesystemProtoP\001\242\002\003FXX\252\002\nFilesystem\312\002\nFilesystem\342\002\026Filesystem\\GPBMetadata\352\002\nFilesystem" - _globals["_FILETYPE"]._serialized_start = 1388 - _globals["_FILETYPE"]._serialized_end = 1470 - _globals["_EVENTTYPE"]._serialized_start = 1473 - _globals["_EVENTTYPE"]._serialized_end = 1625 + _globals["_FILETYPE"]._serialized_start = 1410 + _globals["_FILETYPE"]._serialized_end = 1492 + _globals["_EVENTTYPE"]._serialized_start = 1495 + _globals["_EVENTTYPE"]._serialized_end = 1647 _globals["_MOVEREQUEST"]._serialized_start = 43 _globals["_MOVEREQUEST"]._serialized_end = 114 _globals["_MOVERESPONSE"]._serialized_start = 116 @@ -55,31 +55,31 @@ _globals["_ENTRYINFO"]._serialized_start = 430 _globals["_ENTRYINFO"]._serialized_end = 523 _globals["_LISTDIRREQUEST"]._serialized_start = 525 - _globals["_LISTDIRREQUEST"]._serialized_end = 561 - _globals["_LISTDIRRESPONSE"]._serialized_start = 563 - _globals["_LISTDIRRESPONSE"]._serialized_end = 629 - _globals["_WATCHDIRREQUEST"]._serialized_start = 631 - _globals["_WATCHDIRREQUEST"]._serialized_end = 698 - _globals["_FILESYSTEMEVENT"]._serialized_start = 700 - _globals["_FILESYSTEMEVENT"]._serialized_end = 780 - _globals["_WATCHDIRRESPONSE"]._serialized_start = 783 - _globals["_WATCHDIRRESPONSE"]._serialized_end = 1037 - _globals["_WATCHDIRRESPONSE_STARTEVENT"]._serialized_start = 1003 - _globals["_WATCHDIRRESPONSE_STARTEVENT"]._serialized_end = 1015 - _globals["_WATCHDIRRESPONSE_KEEPALIVE"]._serialized_start = 1017 - _globals["_WATCHDIRRESPONSE_KEEPALIVE"]._serialized_end = 1028 - _globals["_CREATEWATCHERREQUEST"]._serialized_start = 1039 - _globals["_CREATEWATCHERREQUEST"]._serialized_end = 1111 - _globals["_CREATEWATCHERRESPONSE"]._serialized_start = 1113 - _globals["_CREATEWATCHERRESPONSE"]._serialized_end = 1167 - _globals["_GETWATCHEREVENTSREQUEST"]._serialized_start = 1169 - _globals["_GETWATCHEREVENTSREQUEST"]._serialized_end = 1225 - _globals["_GETWATCHEREVENTSRESPONSE"]._serialized_start = 1227 - _globals["_GETWATCHEREVENTSRESPONSE"]._serialized_end = 1306 - _globals["_REMOVEWATCHERREQUEST"]._serialized_start = 1308 - _globals["_REMOVEWATCHERREQUEST"]._serialized_end = 1361 - _globals["_REMOVEWATCHERRESPONSE"]._serialized_start = 1363 - _globals["_REMOVEWATCHERRESPONSE"]._serialized_end = 1386 - _globals["_FILESYSTEM"]._serialized_start = 1628 - _globals["_FILESYSTEM"]._serialized_end = 2299 + _globals["_LISTDIRREQUEST"]._serialized_end = 583 + _globals["_LISTDIRRESPONSE"]._serialized_start = 585 + _globals["_LISTDIRRESPONSE"]._serialized_end = 651 + _globals["_WATCHDIRREQUEST"]._serialized_start = 653 + _globals["_WATCHDIRREQUEST"]._serialized_end = 720 + _globals["_FILESYSTEMEVENT"]._serialized_start = 722 + _globals["_FILESYSTEMEVENT"]._serialized_end = 802 + _globals["_WATCHDIRRESPONSE"]._serialized_start = 805 + _globals["_WATCHDIRRESPONSE"]._serialized_end = 1059 + _globals["_WATCHDIRRESPONSE_STARTEVENT"]._serialized_start = 1025 + _globals["_WATCHDIRRESPONSE_STARTEVENT"]._serialized_end = 1037 + _globals["_WATCHDIRRESPONSE_KEEPALIVE"]._serialized_start = 1039 + _globals["_WATCHDIRRESPONSE_KEEPALIVE"]._serialized_end = 1050 + _globals["_CREATEWATCHERREQUEST"]._serialized_start = 1061 + _globals["_CREATEWATCHERREQUEST"]._serialized_end = 1133 + _globals["_CREATEWATCHERRESPONSE"]._serialized_start = 1135 + _globals["_CREATEWATCHERRESPONSE"]._serialized_end = 1189 + _globals["_GETWATCHEREVENTSREQUEST"]._serialized_start = 1191 + _globals["_GETWATCHEREVENTSREQUEST"]._serialized_end = 1247 + _globals["_GETWATCHEREVENTSRESPONSE"]._serialized_start = 1249 + _globals["_GETWATCHEREVENTSRESPONSE"]._serialized_end = 1328 + _globals["_REMOVEWATCHERREQUEST"]._serialized_start = 1330 + _globals["_REMOVEWATCHERREQUEST"]._serialized_end = 1383 + _globals["_REMOVEWATCHERRESPONSE"]._serialized_start = 1385 + _globals["_REMOVEWATCHERRESPONSE"]._serialized_end = 1408 + _globals["_FILESYSTEM"]._serialized_start = 1650 + _globals["_FILESYSTEM"]._serialized_end = 2321 # @@protoc_insertion_point(module_scope) diff --git a/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.pyi b/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.pyi index c031b13ed4..0b93efc1d6 100644 --- a/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.pyi +++ b/packages/python-sdk/e2b/envd/filesystem/filesystem_pb2.pyi @@ -103,10 +103,14 @@ class EntryInfo(_message.Message): ) -> None: ... class ListDirRequest(_message.Message): - __slots__ = ("path",) + __slots__ = ("path", "depth") PATH_FIELD_NUMBER: _ClassVar[int] + DEPTH_FIELD_NUMBER: _ClassVar[int] path: str - def __init__(self, path: _Optional[str] = ...) -> None: ... + depth: int + def __init__( + self, path: _Optional[str] = ..., depth: _Optional[int] = ... + ) -> None: ... class ListDirResponse(_message.Message): __slots__ = ("entries",) From fe3a2f6399c89dc8456ac2a9566a83ed00c4d11a Mon Sep 17 00:00:00 2001 From: 0div Date: Mon, 14 Apr 2025 14:37:39 -0700 Subject: [PATCH 05/22] remove dev test file --- .../js-sdk/tests/integration/next.test.ts | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 packages/js-sdk/tests/integration/next.test.ts diff --git a/packages/js-sdk/tests/integration/next.test.ts b/packages/js-sdk/tests/integration/next.test.ts deleted file mode 100644 index e0a795b152..0000000000 --- a/packages/js-sdk/tests/integration/next.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { expect } from 'vitest' - -import { sandboxTest, isDebug, isIntegrationTest } from '../setup.js' -import { Sandbox } from '../../src' -import { wait } from '../setup.js' - -sandboxTest('kill existing sandbox', async ({ sandbox }) => { - const sbx = await Sandbox.create('base', { timeoutMs: 120_000 }) - - const handle = await sbx.commands.run('sleep 10', { background: true }) - await handle.kill() - - //expect(handle.stdout).toBe('') - //expect(handle.stderr).toBe('') - - const psHandle = await sbx.commands.run('ps aux', { background: true }) - console.log(psHandle.stdout) - - await wait(10_000) -}) From f57fdeb47bccadac8acbd036f42c4c3f8691f53a Mon Sep 17 00:00:00 2001 From: 0div Date: Mon, 14 Apr 2025 15:14:28 -0700 Subject: [PATCH 06/22] update js-sdk tests to loop and test -1 depth --- .../js-sdk/tests/integration/next.test.ts | 20 +++ .../js-sdk/tests/sandbox/files/list.test.ts | 119 ++++++++++++------ 2 files changed, 99 insertions(+), 40 deletions(-) create mode 100644 packages/js-sdk/tests/integration/next.test.ts diff --git a/packages/js-sdk/tests/integration/next.test.ts b/packages/js-sdk/tests/integration/next.test.ts new file mode 100644 index 0000000000..e0a795b152 --- /dev/null +++ b/packages/js-sdk/tests/integration/next.test.ts @@ -0,0 +1,20 @@ +import { expect } from 'vitest' + +import { sandboxTest, isDebug, isIntegrationTest } from '../setup.js' +import { Sandbox } from '../../src' +import { wait } from '../setup.js' + +sandboxTest('kill existing sandbox', async ({ sandbox }) => { + const sbx = await Sandbox.create('base', { timeoutMs: 120_000 }) + + const handle = await sbx.commands.run('sleep 10', { background: true }) + await handle.kill() + + //expect(handle.stdout).toBe('') + //expect(handle.stderr).toBe('') + + const psHandle = await sbx.commands.run('ps aux', { background: true }) + console.log(psHandle.stdout) + + await wait(10_000) +}) diff --git a/packages/js-sdk/tests/sandbox/files/list.test.ts b/packages/js-sdk/tests/sandbox/files/list.test.ts index eb1f21de3b..6b26f8cdfd 100644 --- a/packages/js-sdk/tests/sandbox/files/list.test.ts +++ b/packages/js-sdk/tests/sandbox/files/list.test.ts @@ -13,48 +13,87 @@ sandboxTest('list directory', async ({ sandbox }) => { await sandbox.files.makeDir(`${parentDirName}/subdir2/subdir2_1`) await sandbox.files.makeDir(`${parentDirName}/subdir2/subdir2_2`) - // explicit depth 0 (should default to 1) - const files0 = await sandbox.files.list(parentDirName, { depth: 0 }) - console.log(files0) - assert.equal(files0.length, 2) - assert.equal(files0[0].name, 'subdir1') - assert.equal(files0[1].name, 'subdir2') + const testCases = [ + { + name: 'explicit depth 0 (should default to 1)', + depth: 0, + expectedLen: 2, + expectedFiles: ['subdir1', 'subdir2'], + }, + { + name: 'default depth (1)', + depth: undefined, + expectedLen: 2, + expectedFiles: ['subdir1', 'subdir2'], + }, + { + name: 'explicit depth 1', + depth: 1, + expectedLen: 2, + expectedFiles: ['subdir1', 'subdir2'], + }, + { + name: 'explicit depth 2', + depth: 2, + expectedLen: 6, + expectedFiles: [ + 'subdir1', + 'subdir1_1', + 'subdir1_2', + 'subdir2', + 'subdir2_1', + 'subdir2_2', + ], + }, + { + name: 'explicit depth 3 (should be the same as depth 2)', + depth: 3, + expectedLen: 6, + expectedFiles: [ + 'subdir1', + 'subdir1_1', + 'subdir1_2', + 'subdir2', + 'subdir2_1', + 'subdir2_2', + ], + }, + { + name: 'negative depth should error', + depth: -1, + expectedLen: 0, + expectedFiles: [], + expectError: 'invalid_argument', + }, + ] - // default depth (1) - const files = await sandbox.files.list(parentDirName) - console.log(files) - assert.equal(files.length, 2) - assert.equal(files[0].name, 'subdir1') - assert.equal(files[1].name, 'subdir2') + for (const testCase of testCases) { + if (testCase.expectError) { + try { + await sandbox.files.list( + parentDirName, + testCase.depth !== undefined ? { depth: testCase.depth } : undefined + ) + assert.fail('Expected error but none was thrown') + } catch (err) { + assert.ok( + err.message.includes(testCase.expectError), + `expected error message to include "${testCase.expectError}"` + ) + continue + } + } else { + const files = await sandbox.files.list( + parentDirName, + testCase.depth !== undefined ? { depth: testCase.depth } : undefined + ) + assert.equal(files.length, testCase.expectedLen) - // explicit depth 1 - const files1 = await sandbox.files.list(parentDirName, { depth: 1 }) - console.log(files1) - assert.equal(files1.length, 2) - assert.equal(files1[0].name, 'subdir1') - assert.equal(files1[1].name, 'subdir2') - - // explicit depth 2 - const files2 = await sandbox.files.list(parentDirName, { depth: 2 }) - console.log(files2) - assert.equal(files2.length, 6) - assert.equal(files2[0].name, 'subdir1') - assert.equal(files2[1].name, 'subdir1_1') - assert.equal(files2[2].name, 'subdir1_2') - assert.equal(files2[3].name, 'subdir2') - assert.equal(files2[4].name, 'subdir2_1') - assert.equal(files2[5].name, 'subdir2_2') - - // explicit depth 3 (should be the same as depth 2) - const files3 = await sandbox.files.list(parentDirName, { depth: 3 }) - console.log(files3) - assert.equal(files3.length, 6) - assert.equal(files3[0].name, 'subdir1') - assert.equal(files3[1].name, 'subdir1_1') - assert.equal(files3[2].name, 'subdir1_2') - assert.equal(files3[3].name, 'subdir2') - assert.equal(files3[4].name, 'subdir2_1') - assert.equal(files3[5].name, 'subdir2_2') + for (let i = 0; i < testCase.expectedFiles.length; i++) { + assert.equal(files[i].name, testCase.expectedFiles[i]) + } + } + } onTestFinished(() => { sandbox.files.remove(parentDirName) From 427822ce4d8a256da5a54373c7949a9331e1d2b3 Mon Sep 17 00:00:00 2001 From: 0div Date: Mon, 14 Apr 2025 15:47:22 -0700 Subject: [PATCH 07/22] update python-sdk tests to loop and assert throw for depth out of range --- .../sandbox_async/files/test_files_list.py | 119 ++++++++++++------ .../sandbox_sync/files/test_files_list.py | 119 ++++++++++++------ 2 files changed, 164 insertions(+), 74 deletions(-) diff --git a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py index 2c34bc855f..c8b41e1409 100644 --- a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py +++ b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py @@ -14,43 +14,88 @@ async def test_list_directory(async_sandbox: AsyncSandbox): await async_sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_1") await async_sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_2") - # explicit depth 0 (should default to 1) - files0 = await async_sandbox.files.list(parent_dir_name, depth=0) - assert len(files0) == 2 - assert files0[0].name == "subdir1" - assert files0[1].name == "subdir2" - - # default depth (1) - files = await async_sandbox.files.list(parent_dir_name) - assert len(files) == 2 - assert files[0].name == "subdir1" - assert files[1].name == "subdir2" - - # explicit depth 1 - files1 = await async_sandbox.files.list(parent_dir_name, depth=1) - assert len(files1) == 2 - assert files1[0].name == "subdir1" - assert files1[1].name == "subdir2" - - # explicit depth 2 - files2 = await async_sandbox.files.list(parent_dir_name, depth=2) - assert len(files2) == 6 - assert files2[0].name == "subdir1" - assert files2[1].name == "subdir1_1" - assert files2[2].name == "subdir1_2" - assert files2[3].name == "subdir2" - assert files2[4].name == "subdir2_1" - assert files2[5].name == "subdir2_2" - - # explicit depth 3 (should be the same as depth 2) - files3 = await async_sandbox.files.list(parent_dir_name, depth=3) - assert len(files3) == 6 - assert files3[0].name == "subdir1" - assert files3[1].name == "subdir1_1" - assert files3[2].name == "subdir1_2" - assert files3[3].name == "subdir2" - assert files3[4].name == "subdir2_1" - assert files3[5].name == "subdir2_2" + test_cases = [ + { + "name": "explicit depth 0 (should default to 1)", + "depth": 0, + "expected_len": 2, + "expected_files": ["subdir1", "subdir2"], + }, + { + "name": "default depth (1)", + "depth": None, + "expected_len": 2, + "expected_files": ["subdir1", "subdir2"], + }, + { + "name": "explicit depth 1", + "depth": 1, + "expected_len": 2, + "expected_files": ["subdir1", "subdir2"], + }, + { + "name": "explicit depth 2", + "depth": 2, + "expected_len": 6, + "expected_files": [ + "subdir1", + "subdir1_1", + "subdir1_2", + "subdir2", + "subdir2_1", + "subdir2_2", + ], + }, + { + "name": "explicit depth 3 (should be the same as depth 2)", + "depth": 3, + "expected_len": 6, + "expected_files": [ + "subdir1", + "subdir1_1", + "subdir1_2", + "subdir2", + "subdir2_1", + "subdir2_2", + ], + }, + { + "name": "negative depth should error", + "depth": -1, + "expected_len": 0, + "expected_files": [], + "expect_error": "Value out of range", + }, + ] + + for test_case in test_cases: + if "expect_error" in test_case: + try: + await async_sandbox.files.list( + parent_dir_name, + depth=( + test_case["depth"] if test_case["depth"] is not None else None + ), + ) + assert False, "Expected error but none was thrown" + except Exception as err: + assert test_case["expect_error"] in str( + err + ), f'expected error message to include "{test_case["expect_error"]}"' + continue + else: + # Get files list with specified depth (or default if None) + files = await async_sandbox.files.list( + parent_dir_name, + depth=test_case["depth"] if test_case["depth"] is not None else None, + ) + + # Verify number of files + assert len(files) == test_case["expected_len"] + + # Verify file names match expected order + for i, expected_name in enumerate(test_case["expected_files"]): + assert files[i].name == expected_name # Cleanup await async_sandbox.files.remove(parent_dir_name) diff --git a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py index d10cd1086d..4abecde3a3 100644 --- a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py +++ b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py @@ -14,43 +14,88 @@ def test_list_directory(sandbox: Sandbox): sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_1") sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_2") - # explicit depth 0 (should default to 1) - files0 = sandbox.files.list(parent_dir_name, depth=0) - assert len(files0) == 2 - assert files0[0].name == "subdir1" - assert files0[1].name == "subdir2" - - # default depth (1) - files = sandbox.files.list(parent_dir_name) - assert len(files) == 2 - assert files[0].name == "subdir1" - assert files[1].name == "subdir2" - - # explicit depth 1 - files1 = sandbox.files.list(parent_dir_name, depth=1) - assert len(files1) == 2 - assert files1[0].name == "subdir1" - assert files1[1].name == "subdir2" - - # explicit depth 2 - files2 = sandbox.files.list(parent_dir_name, depth=2) - assert len(files2) == 6 - assert files2[0].name == "subdir1" - assert files2[1].name == "subdir1_1" - assert files2[2].name == "subdir1_2" - assert files2[3].name == "subdir2" - assert files2[4].name == "subdir2_1" - assert files2[5].name == "subdir2_2" - - # explicit depth 3 (should be the same as depth 2) - files3 = sandbox.files.list(parent_dir_name, depth=3) - assert len(files3) == 6 - assert files3[0].name == "subdir1" - assert files3[1].name == "subdir1_1" - assert files3[2].name == "subdir1_2" - assert files3[3].name == "subdir2" - assert files3[4].name == "subdir2_1" - assert files3[5].name == "subdir2_2" + test_cases = [ + { + "name": "explicit depth 0 (should default to 1)", + "depth": 0, + "expected_len": 2, + "expected_files": ["subdir1", "subdir2"], + }, + { + "name": "default depth (1)", + "depth": None, + "expected_len": 2, + "expected_files": ["subdir1", "subdir2"], + }, + { + "name": "explicit depth 1", + "depth": 1, + "expected_len": 2, + "expected_files": ["subdir1", "subdir2"], + }, + { + "name": "explicit depth 2", + "depth": 2, + "expected_len": 6, + "expected_files": [ + "subdir1", + "subdir1_1", + "subdir1_2", + "subdir2", + "subdir2_1", + "subdir2_2", + ], + }, + { + "name": "explicit depth 3 (should be the same as depth 2)", + "depth": 3, + "expected_len": 6, + "expected_files": [ + "subdir1", + "subdir1_1", + "subdir1_2", + "subdir2", + "subdir2_1", + "subdir2_2", + ], + }, + { + "name": "negative depth should error", + "depth": -1, + "expected_len": 0, + "expected_files": [], + "expect_error": "Value out of range", + }, + ] + + for test_case in test_cases: + if "expect_error" in test_case: + try: + sandbox.files.list( + parent_dir_name, + depth=( + test_case["depth"] if test_case["depth"] is not None else None + ), + ) + assert False, "Expected error but none was thrown" + except Exception as err: + assert test_case["expect_error"] in str( + err + ), f'expected error message to include "{test_case["expect_error"]}"' + continue + else: + # Get files list with specified depth (or default if None) + files = sandbox.files.list( + parent_dir_name, + depth=test_case["depth"] if test_case["depth"] is not None else None, + ) + + # Verify number of files + assert len(files) == test_case["expected_len"] + + # Verify file names match expected order + for i, expected_name in enumerate(test_case["expected_files"]): + assert files[i].name == expected_name # Cleanup sandbox.files.remove(parent_dir_name) From d9349f65844bfa472114a375bd1fcbe36f8efc0b Mon Sep 17 00:00:00 2001 From: 0div Date: Wed, 16 Apr 2025 14:52:06 -0700 Subject: [PATCH 08/22] extend FilesystemOpts to include optional depth --- packages/js-sdk/src/sandbox/filesystem/index.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/js-sdk/src/sandbox/filesystem/index.ts b/packages/js-sdk/src/sandbox/filesystem/index.ts index 5889077065..df0b8853cf 100644 --- a/packages/js-sdk/src/sandbox/filesystem/index.ts +++ b/packages/js-sdk/src/sandbox/filesystem/index.ts @@ -85,6 +85,12 @@ export interface FilesystemRequestOpts * This affects the resolution of relative paths and ownership of the created filesystem objects. */ user?: Username + /** + * Depth of the directory to list. + * + * @default 1 + */ + depth?: number } /** @@ -339,10 +345,7 @@ export class Filesystem { * * @returns list of entries in the sandbox filesystem directory. */ - async list( - path: string, - opts?: FilesystemRequestOpts & { depth?: number } - ): Promise { + async list(path: string, opts?: FilesystemRequestOpts): Promise { try { const res = await this.rpc.listDir( { From 87967346f029a534d5379a08d50340ec68365981 Mon Sep 17 00:00:00 2001 From: 0div Date: Thu, 17 Apr 2025 15:06:05 -0700 Subject: [PATCH 09/22] js-sdk: make depth seperate param; better error message; update tests --- .../js-sdk/src/sandbox/filesystem/index.ts | 21 +++++++++++-------- .../js-sdk/tests/sandbox/files/list.test.ts | 6 +++--- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/js-sdk/src/sandbox/filesystem/index.ts b/packages/js-sdk/src/sandbox/filesystem/index.ts index df0b8853cf..b12accbba0 100644 --- a/packages/js-sdk/src/sandbox/filesystem/index.ts +++ b/packages/js-sdk/src/sandbox/filesystem/index.ts @@ -26,7 +26,7 @@ import { import { FilesystemEvent, WatchHandle } from './watchHandle' import { compareVersions } from 'compare-versions' -import { TemplateError } from '../../errors' +import { InvalidArgumentError, TemplateError } from '../../errors' import { ENVD_VERSION_RECURSIVE_WATCH } from '../../envd/versions' /** @@ -85,12 +85,6 @@ export interface FilesystemRequestOpts * This affects the resolution of relative paths and ownership of the created filesystem objects. */ user?: Username - /** - * Depth of the directory to list. - * - * @default 1 - */ - depth?: number } /** @@ -341,16 +335,25 @@ export class Filesystem { * List entries in a directory. * * @param path path to the directory. + * @param depth depth of the directory to list. * @param opts connection options. * * @returns list of entries in the sandbox filesystem directory. */ - async list(path: string, opts?: FilesystemRequestOpts): Promise { + async list( + path: string, + depth?: number, + opts?: FilesystemRequestOpts + ): Promise { + if (typeof depth === 'number' && depth < 0) { + throw new InvalidArgumentError('depth should be a positive number') + } + try { const res = await this.rpc.listDir( { path, - depth: opts?.depth ?? 1, + depth: depth ?? 1, }, { headers: authenticationHeader(opts?.user), diff --git a/packages/js-sdk/tests/sandbox/files/list.test.ts b/packages/js-sdk/tests/sandbox/files/list.test.ts index 6b26f8cdfd..ee68377995 100644 --- a/packages/js-sdk/tests/sandbox/files/list.test.ts +++ b/packages/js-sdk/tests/sandbox/files/list.test.ts @@ -63,7 +63,7 @@ sandboxTest('list directory', async ({ sandbox }) => { depth: -1, expectedLen: 0, expectedFiles: [], - expectError: 'invalid_argument', + expectError: 'depth should be a positive number', }, ] @@ -72,7 +72,7 @@ sandboxTest('list directory', async ({ sandbox }) => { try { await sandbox.files.list( parentDirName, - testCase.depth !== undefined ? { depth: testCase.depth } : undefined + testCase.depth !== undefined ? testCase.depth : undefined ) assert.fail('Expected error but none was thrown') } catch (err) { @@ -85,7 +85,7 @@ sandboxTest('list directory', async ({ sandbox }) => { } else { const files = await sandbox.files.list( parentDirName, - testCase.depth !== undefined ? { depth: testCase.depth } : undefined + testCase.depth !== undefined ? testCase.depth : undefined ) assert.equal(files.length, testCase.expectedLen) From c588a36929cceb0a2c099faa26470ee89d180bbf Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 21 Apr 2025 18:38:50 +0200 Subject: [PATCH 10/22] extend FilesystemListOpts --- .../js-sdk/src/sandbox/filesystem/index.ts | 18 ++++++++++-------- .../js-sdk/tests/sandbox/files/list.test.ts | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/js-sdk/src/sandbox/filesystem/index.ts b/packages/js-sdk/src/sandbox/filesystem/index.ts index b12accbba0..d1258c8b5a 100644 --- a/packages/js-sdk/src/sandbox/filesystem/index.ts +++ b/packages/js-sdk/src/sandbox/filesystem/index.ts @@ -87,6 +87,13 @@ export interface FilesystemRequestOpts user?: Username } +export interface FilesystemListOpts extends FilesystemRequestOpts { + /** + * Depth of the directory to list. + */ + depth?: number +} + /** * Options for watching a directory. */ @@ -335,17 +342,12 @@ export class Filesystem { * List entries in a directory. * * @param path path to the directory. - * @param depth depth of the directory to list. * @param opts connection options. * * @returns list of entries in the sandbox filesystem directory. */ - async list( - path: string, - depth?: number, - opts?: FilesystemRequestOpts - ): Promise { - if (typeof depth === 'number' && depth < 0) { + async list(path: string, opts?: FilesystemListOpts): Promise { + if (typeof opts?.depth === 'number' && opts.depth < 0) { throw new InvalidArgumentError('depth should be a positive number') } @@ -353,7 +355,7 @@ export class Filesystem { const res = await this.rpc.listDir( { path, - depth: depth ?? 1, + depth: opts?.depth ?? 1, }, { headers: authenticationHeader(opts?.user), diff --git a/packages/js-sdk/tests/sandbox/files/list.test.ts b/packages/js-sdk/tests/sandbox/files/list.test.ts index ee68377995..4147aeb6a3 100644 --- a/packages/js-sdk/tests/sandbox/files/list.test.ts +++ b/packages/js-sdk/tests/sandbox/files/list.test.ts @@ -72,7 +72,7 @@ sandboxTest('list directory', async ({ sandbox }) => { try { await sandbox.files.list( parentDirName, - testCase.depth !== undefined ? testCase.depth : undefined + testCase.depth !== undefined ? { depth: testCase.depth } : undefined ) assert.fail('Expected error but none was thrown') } catch (err) { @@ -85,7 +85,7 @@ sandboxTest('list directory', async ({ sandbox }) => { } else { const files = await sandbox.files.list( parentDirName, - testCase.depth !== undefined ? testCase.depth : undefined + testCase.depth !== undefined ? { depth: testCase.depth } : undefined ) assert.equal(files.length, testCase.expectedLen) From 7fbccd5f34e3aefa4b3adc2f5e487850ff4419f4 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 21 Apr 2025 18:46:17 +0200 Subject: [PATCH 11/22] split files list test (js) --- .../js-sdk/tests/sandbox/files/list.test.ts | 59 +++++++++---------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/packages/js-sdk/tests/sandbox/files/list.test.ts b/packages/js-sdk/tests/sandbox/files/list.test.ts index 4147aeb6a3..f2975d3282 100644 --- a/packages/js-sdk/tests/sandbox/files/list.test.ts +++ b/packages/js-sdk/tests/sandbox/files/list.test.ts @@ -2,9 +2,9 @@ import { assert, onTestFinished } from 'vitest' import { sandboxTest } from '../../setup.js' -sandboxTest('list directory', async ({ sandbox }) => { - const parentDirName = 'test_directory' +const parentDirName = 'test_directory' +sandboxTest('list directory', async ({ sandbox }) => { await sandbox.files.makeDir(parentDirName) await sandbox.files.makeDir(`${parentDirName}/subdir1`) await sandbox.files.makeDir(`${parentDirName}/subdir2`) @@ -58,40 +58,17 @@ sandboxTest('list directory', async ({ sandbox }) => { 'subdir2_2', ], }, - { - name: 'negative depth should error', - depth: -1, - expectedLen: 0, - expectedFiles: [], - expectError: 'depth should be a positive number', - }, ] for (const testCase of testCases) { - if (testCase.expectError) { - try { - await sandbox.files.list( - parentDirName, - testCase.depth !== undefined ? { depth: testCase.depth } : undefined - ) - assert.fail('Expected error but none was thrown') - } catch (err) { - assert.ok( - err.message.includes(testCase.expectError), - `expected error message to include "${testCase.expectError}"` - ) - continue - } - } else { - const files = await sandbox.files.list( - parentDirName, - testCase.depth !== undefined ? { depth: testCase.depth } : undefined - ) - assert.equal(files.length, testCase.expectedLen) + const files = await sandbox.files.list( + parentDirName, + testCase.depth !== undefined ? { depth: testCase.depth } : undefined + ) + assert.equal(files.length, testCase.expectedLen) - for (let i = 0; i < testCase.expectedFiles.length; i++) { - assert.equal(files[i].name, testCase.expectedFiles[i]) - } + for (let i = 0; i < testCase.expectedFiles.length; i++) { + assert.equal(files[i].name, testCase.expectedFiles[i]) } } @@ -99,3 +76,21 @@ sandboxTest('list directory', async ({ sandbox }) => { sandbox.files.remove(parentDirName) }) }) + +sandboxTest('list directory with invalid depth', async ({ sandbox }) => { + await sandbox.files.makeDir(parentDirName) + + try { + await sandbox.files.list(parentDirName, { depth: -1 }) + assert.fail('Expected error but none was thrown') + } catch (err) { + assert.ok( + err.message.includes('depth should be a positive number'), + 'expected error message to include "depth should be a positive number"' + ) + } + + onTestFinished(() => { + sandbox.files.remove(parentDirName) + }) +}) From b831a64818efdd83211d486c5f46cd2b4f723833 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 21 Apr 2025 18:53:31 +0200 Subject: [PATCH 12/22] split files.list tests (py) --- .../sandbox_async/files/test_files_list.py | 55 +++++++----------- .../sandbox_sync/files/test_files_list.py | 57 ++++++++----------- 2 files changed, 45 insertions(+), 67 deletions(-) diff --git a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py index c8b41e1409..7d01f40cfc 100644 --- a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py +++ b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py @@ -59,43 +59,32 @@ async def test_list_directory(async_sandbox: AsyncSandbox): "subdir2_2", ], }, - { - "name": "negative depth should error", - "depth": -1, - "expected_len": 0, - "expected_files": [], - "expect_error": "Value out of range", - }, ] for test_case in test_cases: - if "expect_error" in test_case: - try: - await async_sandbox.files.list( - parent_dir_name, - depth=( - test_case["depth"] if test_case["depth"] is not None else None - ), - ) - assert False, "Expected error but none was thrown" - except Exception as err: - assert test_case["expect_error"] in str( - err - ), f'expected error message to include "{test_case["expect_error"]}"' - continue - else: - # Get files list with specified depth (or default if None) - files = await async_sandbox.files.list( - parent_dir_name, - depth=test_case["depth"] if test_case["depth"] is not None else None, - ) + files = await async_sandbox.files.list( + parent_dir_name, + depth=test_case["depth"] if test_case["depth"] is not None else None, + ) + + assert len(files) == test_case["expected_len"] - # Verify number of files - assert len(files) == test_case["expected_len"] + for i, expected_name in enumerate(test_case["expected_files"]): + assert files[i].name == expected_name + + await async_sandbox.files.remove(parent_dir_name) + + +async def test_list_directory_error_cases(async_sandbox: AsyncSandbox): + parent_dir_name = f"test_directory_{uuid.uuid4()}" + await async_sandbox.files.make_dir(parent_dir_name) - # Verify file names match expected order - for i, expected_name in enumerate(test_case["expected_files"]): - assert files[i].name == expected_name + try: + await async_sandbox.files.list(parent_dir_name, depth=-1) + assert False, "Expected error but none was thrown" + except Exception as err: + assert ( + "Value out of range" in str(err) + ), 'expected error message to include "Value out of range"' - # Cleanup await async_sandbox.files.remove(parent_dir_name) diff --git a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py index 4abecde3a3..c915715c6d 100644 --- a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py +++ b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py @@ -1,6 +1,6 @@ import uuid -from e2b import Sandbox, FileType +from e2b import Sandbox def test_list_directory(sandbox: Sandbox): @@ -59,43 +59,32 @@ def test_list_directory(sandbox: Sandbox): "subdir2_2", ], }, - { - "name": "negative depth should error", - "depth": -1, - "expected_len": 0, - "expected_files": [], - "expect_error": "Value out of range", - }, ] for test_case in test_cases: - if "expect_error" in test_case: - try: - sandbox.files.list( - parent_dir_name, - depth=( - test_case["depth"] if test_case["depth"] is not None else None - ), - ) - assert False, "Expected error but none was thrown" - except Exception as err: - assert test_case["expect_error"] in str( - err - ), f'expected error message to include "{test_case["expect_error"]}"' - continue - else: - # Get files list with specified depth (or default if None) - files = sandbox.files.list( - parent_dir_name, - depth=test_case["depth"] if test_case["depth"] is not None else None, - ) + files = sandbox.files.list( + parent_dir_name, + depth=test_case["depth"] if test_case["depth"] is not None else None, + ) + + assert len(files) == test_case["expected_len"] - # Verify number of files - assert len(files) == test_case["expected_len"] + for i, expected_name in enumerate(test_case["expected_files"]): + assert files[i].name == expected_name + + sandbox.files.remove(parent_dir_name) + + +def test_list_directory_error_cases(sandbox: Sandbox): + parent_dir_name = f"test_directory_{uuid.uuid4()}" + sandbox.files.make_dir(parent_dir_name) - # Verify file names match expected order - for i, expected_name in enumerate(test_case["expected_files"]): - assert files[i].name == expected_name + try: + sandbox.files.list(parent_dir_name, depth=-1) + assert False, "Expected error but none was thrown" + except Exception as err: + assert ( + "Value out of range" in str(err) + ), 'expected error message to include "Value out of range"' - # Cleanup sandbox.files.remove(parent_dir_name) From 0a7ae108c465f437704e366f8360abfb0603c25e Mon Sep 17 00:00:00 2001 From: 0div Date: Mon, 21 Apr 2025 10:46:55 -0700 Subject: [PATCH 13/22] rm next test --- .../js-sdk/tests/integration/next.test.ts | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 packages/js-sdk/tests/integration/next.test.ts diff --git a/packages/js-sdk/tests/integration/next.test.ts b/packages/js-sdk/tests/integration/next.test.ts deleted file mode 100644 index e0a795b152..0000000000 --- a/packages/js-sdk/tests/integration/next.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { expect } from 'vitest' - -import { sandboxTest, isDebug, isIntegrationTest } from '../setup.js' -import { Sandbox } from '../../src' -import { wait } from '../setup.js' - -sandboxTest('kill existing sandbox', async ({ sandbox }) => { - const sbx = await Sandbox.create('base', { timeoutMs: 120_000 }) - - const handle = await sbx.commands.run('sleep 10', { background: true }) - await handle.kill() - - //expect(handle.stdout).toBe('') - //expect(handle.stderr).toBe('') - - const psHandle = await sbx.commands.run('ps aux', { background: true }) - console.log(psHandle.stdout) - - await wait(10_000) -}) From 838d694ae64bc003081727c706e7b1b8c1656736 Mon Sep 17 00:00:00 2001 From: 0div Date: Mon, 21 Apr 2025 11:17:57 -0700 Subject: [PATCH 14/22] python-sdk: return InvalidArgument for negative depth and update tests --- .../python-sdk/e2b/sandbox_async/filesystem/filesystem.py | 6 ++++-- .../python-sdk/e2b/sandbox_sync/filesystem/filesystem.py | 5 ++++- .../tests/async/sandbox_async/files/test_files_list.py | 7 ++++--- .../tests/sync/sandbox_sync/files/test_files_list.py | 7 ++++--- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py b/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py index a2789d247f..11f04bd431 100644 --- a/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py +++ b/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py @@ -4,7 +4,6 @@ from packaging.version import Version from typing import AsyncIterator, IO, List, Literal, Optional, overload, Union from e2b.sandbox.filesystem.filesystem import WriteEntry - import e2b_connect as connect from e2b.connection_config import ( ConnectionConfig, @@ -16,7 +15,7 @@ from e2b.envd.filesystem import filesystem_connect, filesystem_pb2 from e2b.envd.rpc import authentication_header, handle_rpc_exception from e2b.envd.versions import ENVD_VERSION_RECURSIVE_WATCH -from e2b.exceptions import SandboxException, TemplateException +from e2b.exceptions import SandboxException, TemplateException, InvalidArgumentException from e2b.sandbox.filesystem.filesystem import EntryInfo, map_file_type from e2b.sandbox.filesystem.watch_handle import FilesystemEvent from e2b.sandbox_async.filesystem.watch_handle import AsyncWatchHandle @@ -268,6 +267,9 @@ async def list( :return: List of entries in the directory """ + if depth is not None and depth < 0: + raise InvalidArgumentException("depth should be a positive number") + try: res = await self._rpc.alist_dir( filesystem_pb2.ListDirRequest(path=path, depth=depth), diff --git a/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py b/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py index 11c60a8e8c..628c65fe34 100644 --- a/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py +++ b/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py @@ -8,7 +8,7 @@ from packaging.version import Version from e2b.envd.versions import ENVD_VERSION_RECURSIVE_WATCH -from e2b.exceptions import TemplateException +from e2b.exceptions import TemplateException, InvalidArgumentException from e2b.connection_config import ( ConnectionConfig, Username, @@ -264,6 +264,9 @@ def list( :return: List of entries in the directory """ + if depth is not None and depth < 0: + raise InvalidArgumentException("depth should be a positive number") + try: res = self._rpc.list_dir( filesystem_pb2.ListDirRequest(path=path, depth=depth), diff --git a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py index 7d01f40cfc..18397b2995 100644 --- a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py +++ b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py @@ -79,12 +79,13 @@ async def test_list_directory_error_cases(async_sandbox: AsyncSandbox): parent_dir_name = f"test_directory_{uuid.uuid4()}" await async_sandbox.files.make_dir(parent_dir_name) + expected_error_message = "depth should be a positive number" try: await async_sandbox.files.list(parent_dir_name, depth=-1) assert False, "Expected error but none was thrown" except Exception as err: - assert ( - "Value out of range" in str(err) - ), 'expected error message to include "Value out of range"' + assert expected_error_message in str( + err + ), f'expected error message to include "{expected_error_message}"' await async_sandbox.files.remove(parent_dir_name) diff --git a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py index c915715c6d..f8dc745958 100644 --- a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py +++ b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py @@ -79,12 +79,13 @@ def test_list_directory_error_cases(sandbox: Sandbox): parent_dir_name = f"test_directory_{uuid.uuid4()}" sandbox.files.make_dir(parent_dir_name) + expected_error_message = "depth should be a positive number" try: sandbox.files.list(parent_dir_name, depth=-1) assert False, "Expected error but none was thrown" except Exception as err: - assert ( - "Value out of range" in str(err) - ), 'expected error message to include "Value out of range"' + assert expected_error_message in str( + err + ), f'expected error message to include "{expected_error_message}"' sandbox.files.remove(parent_dir_name) From 9e0e769ab1a0fc2b6768975f3be870b260f9749c Mon Sep 17 00:00:00 2001 From: 0div Date: Mon, 28 Apr 2025 14:24:13 -0700 Subject: [PATCH 15/22] add full paths in python-sdk list tests --- .../e2b/sandbox_sync/filesystem/filesystem.py | 4 +- .../sandbox_async/files/test_files_list.py | 42 +++++++++---------- .../sandbox_sync/files/test_files_list.py | 40 +++++++++--------- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py b/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py index 628c65fe34..1ff9e9bf1a 100644 --- a/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py +++ b/packages/python-sdk/e2b/sandbox_sync/filesystem/filesystem.py @@ -264,8 +264,8 @@ def list( :return: List of entries in the directory """ - if depth is not None and depth < 0: - raise InvalidArgumentException("depth should be a positive number") + if depth is not None and depth < 1: + raise InvalidArgumentException("depth should be at least 1") try: res = self._rpc.list_dir( diff --git a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py index 18397b2995..9d9b3b04eb 100644 --- a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py +++ b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py @@ -15,35 +15,35 @@ async def test_list_directory(async_sandbox: AsyncSandbox): await async_sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_2") test_cases = [ - { - "name": "explicit depth 0 (should default to 1)", - "depth": 0, - "expected_len": 2, - "expected_files": ["subdir1", "subdir2"], - }, { "name": "default depth (1)", "depth": None, "expected_len": 2, - "expected_files": ["subdir1", "subdir2"], + "expected_files": [ + f"{parent_dir_name}/subdir1", + f"{parent_dir_name}/subdir2", + ], }, { "name": "explicit depth 1", "depth": 1, "expected_len": 2, - "expected_files": ["subdir1", "subdir2"], + "expected_files": [ + f"{parent_dir_name}/subdir1", + f"{parent_dir_name}/subdir2", + ], }, { "name": "explicit depth 2", "depth": 2, "expected_len": 6, "expected_files": [ - "subdir1", - "subdir1_1", - "subdir1_2", - "subdir2", - "subdir2_1", - "subdir2_2", + f"{parent_dir_name}/subdir1", + f"{parent_dir_name}/subdir1/subdir1_1", + f"{parent_dir_name}/subdir1/subdir1_2", + f"{parent_dir_name}/subdir2", + f"{parent_dir_name}/subdir2/subdir2_1", + f"{parent_dir_name}/subdir2/subdir2_2", ], }, { @@ -51,12 +51,12 @@ async def test_list_directory(async_sandbox: AsyncSandbox): "depth": 3, "expected_len": 6, "expected_files": [ - "subdir1", - "subdir1_1", - "subdir1_2", - "subdir2", - "subdir2_1", - "subdir2_2", + f"{parent_dir_name}/subdir1", + f"{parent_dir_name}/subdir1/subdir1_1", + f"{parent_dir_name}/subdir1/subdir1_2", + f"{parent_dir_name}/subdir2", + f"{parent_dir_name}/subdir2/subdir2_1", + f"{parent_dir_name}/subdir2/subdir2_2", ], }, ] @@ -79,7 +79,7 @@ async def test_list_directory_error_cases(async_sandbox: AsyncSandbox): parent_dir_name = f"test_directory_{uuid.uuid4()}" await async_sandbox.files.make_dir(parent_dir_name) - expected_error_message = "depth should be a positive number" + expected_error_message = "depth should be at least one" try: await async_sandbox.files.list(parent_dir_name, depth=-1) assert False, "Expected error but none was thrown" diff --git a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py index f8dc745958..4faeef690d 100644 --- a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py +++ b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py @@ -15,35 +15,35 @@ def test_list_directory(sandbox: Sandbox): sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_2") test_cases = [ - { - "name": "explicit depth 0 (should default to 1)", - "depth": 0, - "expected_len": 2, - "expected_files": ["subdir1", "subdir2"], - }, { "name": "default depth (1)", "depth": None, "expected_len": 2, - "expected_files": ["subdir1", "subdir2"], + "expected_files": [ + f"{parent_dir_name}/subdir1", + f"{parent_dir_name}/subdir2", + ], }, { "name": "explicit depth 1", "depth": 1, "expected_len": 2, - "expected_files": ["subdir1", "subdir2"], + "expected_files": [ + f"{parent_dir_name}/subdir1", + f"{parent_dir_name}/subdir2", + ], }, { "name": "explicit depth 2", "depth": 2, "expected_len": 6, "expected_files": [ - "subdir1", - "subdir1_1", - "subdir1_2", - "subdir2", - "subdir2_1", - "subdir2_2", + f"{parent_dir_name}/subdir1", + f"{parent_dir_name}/subdir1/subdir1_1", + f"{parent_dir_name}/subdir1/subdir1_2", + f"{parent_dir_name}/subdir2", + f"{parent_dir_name}/subdir2/subdir2_1", + f"{parent_dir_name}/subdir2/subdir2_2", ], }, { @@ -51,12 +51,12 @@ def test_list_directory(sandbox: Sandbox): "depth": 3, "expected_len": 6, "expected_files": [ - "subdir1", - "subdir1_1", - "subdir1_2", - "subdir2", - "subdir2_1", - "subdir2_2", + f"{parent_dir_name}/subdir1", + f"{parent_dir_name}/subdir1/subdir1_1", + f"{parent_dir_name}/subdir1/subdir1_2", + f"{parent_dir_name}/subdir2", + f"{parent_dir_name}/subdir2/subdir2_1", + f"{parent_dir_name}/subdir2/subdir2_2", ], }, ] From c984a46bba515317a0a07b1f322f345746681d48 Mon Sep 17 00:00:00 2001 From: 0div Date: Mon, 28 Apr 2025 14:27:04 -0700 Subject: [PATCH 16/22] update depthp param validation and tests in js-sdk --- .../js-sdk/src/sandbox/filesystem/index.ts | 4 +-- .../js-sdk/tests/sandbox/files/list.test.ts | 35 ++++++++----------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/packages/js-sdk/src/sandbox/filesystem/index.ts b/packages/js-sdk/src/sandbox/filesystem/index.ts index d1258c8b5a..042831ce8b 100644 --- a/packages/js-sdk/src/sandbox/filesystem/index.ts +++ b/packages/js-sdk/src/sandbox/filesystem/index.ts @@ -347,8 +347,8 @@ export class Filesystem { * @returns list of entries in the sandbox filesystem directory. */ async list(path: string, opts?: FilesystemListOpts): Promise { - if (typeof opts?.depth === 'number' && opts.depth < 0) { - throw new InvalidArgumentError('depth should be a positive number') + if (typeof opts?.depth === 'number' && opts.depth < 1) { + throw new InvalidArgumentError('depth should be a least one') } try { diff --git a/packages/js-sdk/tests/sandbox/files/list.test.ts b/packages/js-sdk/tests/sandbox/files/list.test.ts index f2975d3282..d70a2b65ab 100644 --- a/packages/js-sdk/tests/sandbox/files/list.test.ts +++ b/packages/js-sdk/tests/sandbox/files/list.test.ts @@ -14,12 +14,6 @@ sandboxTest('list directory', async ({ sandbox }) => { await sandbox.files.makeDir(`${parentDirName}/subdir2/subdir2_2`) const testCases = [ - { - name: 'explicit depth 0 (should default to 1)', - depth: 0, - expectedLen: 2, - expectedFiles: ['subdir1', 'subdir2'], - }, { name: 'default depth (1)', depth: undefined, @@ -37,12 +31,12 @@ sandboxTest('list directory', async ({ sandbox }) => { depth: 2, expectedLen: 6, expectedFiles: [ - 'subdir1', - 'subdir1_1', - 'subdir1_2', - 'subdir2', - 'subdir2_1', - 'subdir2_2', + `${parentDirName}/subdir1`, + `${parentDirName}/subdir1/subdir1_1`, + `${parentDirName}/subdir1/subdir1_2`, + `${parentDirName}/subdir2`, + `${parentDirName}/subdir2/subdir2_1`, + `${parentDirName}/subdir2/subdir2_2`, ], }, { @@ -50,12 +44,12 @@ sandboxTest('list directory', async ({ sandbox }) => { depth: 3, expectedLen: 6, expectedFiles: [ - 'subdir1', - 'subdir1_1', - 'subdir1_2', - 'subdir2', - 'subdir2_1', - 'subdir2_2', + `${parentDirName}/subdir1`, + `${parentDirName}/subdir1/subdir1_1`, + `${parentDirName}/subdir1/subdir1_2`, + `${parentDirName}/subdir2`, + `${parentDirName}/subdir2/subdir2_1`, + `${parentDirName}/subdir2/subdir2_2`, ], }, ] @@ -84,9 +78,10 @@ sandboxTest('list directory with invalid depth', async ({ sandbox }) => { await sandbox.files.list(parentDirName, { depth: -1 }) assert.fail('Expected error but none was thrown') } catch (err) { + const expectedErrorMessage = 'depth should be at least one' assert.ok( - err.message.includes('depth should be a positive number'), - 'expected error message to include "depth should be a positive number"' + err.message.includes(expectedErrorMessage), + `expected error message to include "${expectedErrorMessage}"` ) } From ea21f655bb8677f176e8d1835b49f46ec19f3ddb Mon Sep 17 00:00:00 2001 From: 0div Date: Wed, 30 Apr 2025 16:33:01 -0700 Subject: [PATCH 17/22] add missing argument error and tests --- .../python-sdk/e2b/sandbox_async/filesystem/filesystem.py | 4 ++-- .../tests/async/sandbox_async/files/test_files_list.py | 2 +- .../tests/sync/sandbox_sync/files/test_files_list.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py b/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py index 11f04bd431..8d240311c5 100644 --- a/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py +++ b/packages/python-sdk/e2b/sandbox_async/filesystem/filesystem.py @@ -267,8 +267,8 @@ async def list( :return: List of entries in the directory """ - if depth is not None and depth < 0: - raise InvalidArgumentException("depth should be a positive number") + if depth is not None and depth < 1: + raise InvalidArgumentException("depth should be at least 1") try: res = await self._rpc.alist_dir( diff --git a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py index 9d9b3b04eb..04f593921c 100644 --- a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py +++ b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py @@ -79,7 +79,7 @@ async def test_list_directory_error_cases(async_sandbox: AsyncSandbox): parent_dir_name = f"test_directory_{uuid.uuid4()}" await async_sandbox.files.make_dir(parent_dir_name) - expected_error_message = "depth should be at least one" + expected_error_message = "depth should be at least 1" try: await async_sandbox.files.list(parent_dir_name, depth=-1) assert False, "Expected error but none was thrown" diff --git a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py index 4faeef690d..1a1844f3c0 100644 --- a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py +++ b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py @@ -79,7 +79,7 @@ def test_list_directory_error_cases(sandbox: Sandbox): parent_dir_name = f"test_directory_{uuid.uuid4()}" sandbox.files.make_dir(parent_dir_name) - expected_error_message = "depth should be a positive number" + expected_error_message = "depth should be at least 1" try: sandbox.files.list(parent_dir_name, depth=-1) assert False, "Expected error but none was thrown" From 0495b8aa05793f17f9b25ebf274255d93e6a567e Mon Sep 17 00:00:00 2001 From: 0div Date: Wed, 30 Apr 2025 16:34:52 -0700 Subject: [PATCH 18/22] [boyscouting] remove unintented api_ref dir in root folder --- api_ref/sandbox_sync.mdx | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 api_ref/sandbox_sync.mdx diff --git a/api_ref/sandbox_sync.mdx b/api_ref/sandbox_sync.mdx deleted file mode 100644 index e69de29bb2..0000000000 From bedb5d6460b030d94f3a2fcf2127200273972463 Mon Sep 17 00:00:00 2001 From: 0div Date: Thu, 1 May 2025 11:49:29 -0700 Subject: [PATCH 19/22] distinguish between path and name and fix js-sdk tests --- .../js-sdk/src/sandbox/filesystem/index.ts | 2 +- .../js-sdk/tests/sandbox/files/list.test.ts | 74 +++++++++++++------ 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/packages/js-sdk/src/sandbox/filesystem/index.ts b/packages/js-sdk/src/sandbox/filesystem/index.ts index 042831ce8b..46c384fbcd 100644 --- a/packages/js-sdk/src/sandbox/filesystem/index.ts +++ b/packages/js-sdk/src/sandbox/filesystem/index.ts @@ -348,7 +348,7 @@ export class Filesystem { */ async list(path: string, opts?: FilesystemListOpts): Promise { if (typeof opts?.depth === 'number' && opts.depth < 1) { - throw new InvalidArgumentError('depth should be a least one') + throw new InvalidArgumentError('depth should be at least one') } try { diff --git a/packages/js-sdk/tests/sandbox/files/list.test.ts b/packages/js-sdk/tests/sandbox/files/list.test.ts index d70a2b65ab..14a77440fa 100644 --- a/packages/js-sdk/tests/sandbox/files/list.test.ts +++ b/packages/js-sdk/tests/sandbox/files/list.test.ts @@ -4,7 +4,9 @@ import { sandboxTest } from '../../setup.js' const parentDirName = 'test_directory' +/* sandboxTest('list directory', async ({ sandbox }) => { + const homeDirName = '/home/user' await sandbox.files.makeDir(parentDirName) await sandbox.files.makeDir(`${parentDirName}/subdir1`) await sandbox.files.makeDir(`${parentDirName}/subdir2`) @@ -15,41 +17,65 @@ sandboxTest('list directory', async ({ sandbox }) => { const testCases = [ { - name: 'default depth (1)', + test_name: 'default depth (1)', depth: undefined, expectedLen: 2, - expectedFiles: ['subdir1', 'subdir2'], + expectedFileNames: ['subdir1', 'subdir2'], + expectedFilePaths: [ + `${homeDirName}/${parentDirName}/subdir1`, + `${homeDirName}/${parentDirName}/subdir2`, + ], }, { - name: 'explicit depth 1', + test_name: 'explicit depth 1', depth: 1, expectedLen: 2, - expectedFiles: ['subdir1', 'subdir2'], + expectedFileNames: ['subdir1', 'subdir2'], + expectedFilePaths: [ + `${homeDirName}/${parentDirName}/subdir1`, + `${homeDirName}/${parentDirName}/subdir2`, + ], }, { - name: 'explicit depth 2', + test_name: 'explicit depth 2', depth: 2, expectedLen: 6, - expectedFiles: [ - `${parentDirName}/subdir1`, - `${parentDirName}/subdir1/subdir1_1`, - `${parentDirName}/subdir1/subdir1_2`, - `${parentDirName}/subdir2`, - `${parentDirName}/subdir2/subdir2_1`, - `${parentDirName}/subdir2/subdir2_2`, + expectedFileNames: [ + 'subdir1', + 'subdir1_1', + 'subdir1_2', + 'subdir2', + 'subdir2_1', + 'subdir2_2', + ], + expectedFilePaths: [ + `${homeDirName}/${parentDirName}/subdir1`, + `${homeDirName}/${parentDirName}/subdir1/subdir1_1`, + `${homeDirName}/${parentDirName}/subdir1/subdir1_2`, + `${homeDirName}/${parentDirName}/subdir2`, + `${homeDirName}/${parentDirName}/subdir2/subdir2_1`, + `${homeDirName}/${parentDirName}/subdir2/subdir2_2`, ], }, { - name: 'explicit depth 3 (should be the same as depth 2)', + test_name: 'explicit depth 3 (should be the same as depth 2)', depth: 3, expectedLen: 6, - expectedFiles: [ - `${parentDirName}/subdir1`, - `${parentDirName}/subdir1/subdir1_1`, - `${parentDirName}/subdir1/subdir1_2`, - `${parentDirName}/subdir2`, - `${parentDirName}/subdir2/subdir2_1`, - `${parentDirName}/subdir2/subdir2_2`, + expectedFileNames: [ + 'subdir1', + 'subdir1_1', + 'subdir1_2', + 'subdir2', + 'subdir2_1', + 'subdir2_2', + ], + expectedFilePaths: [ + `${homeDirName}/${parentDirName}/subdir1`, + `${homeDirName}/${parentDirName}/subdir1/subdir1_1`, + `${homeDirName}/${parentDirName}/subdir1/subdir1_2`, + `${homeDirName}/${parentDirName}/subdir2`, + `${homeDirName}/${parentDirName}/subdir2/subdir2_1`, + `${homeDirName}/${parentDirName}/subdir2/subdir2_2`, ], }, ] @@ -61,8 +87,10 @@ sandboxTest('list directory', async ({ sandbox }) => { ) assert.equal(files.length, testCase.expectedLen) - for (let i = 0; i < testCase.expectedFiles.length; i++) { - assert.equal(files[i].name, testCase.expectedFiles[i]) + for (let i = 0; i < testCase.expectedFilePaths.length; i++) { + assert.equal(files[i].type, 'dir') + assert.equal(files[i].name, testCase.expectedFileNames[i]) + assert.equal(files[i].path, testCase.expectedFilePaths[i]) } } @@ -70,6 +98,7 @@ sandboxTest('list directory', async ({ sandbox }) => { sandbox.files.remove(parentDirName) }) }) +*/ sandboxTest('list directory with invalid depth', async ({ sandbox }) => { await sandbox.files.makeDir(parentDirName) @@ -79,6 +108,7 @@ sandboxTest('list directory with invalid depth', async ({ sandbox }) => { assert.fail('Expected error but none was thrown') } catch (err) { const expectedErrorMessage = 'depth should be at least one' + console.log(err.message) assert.ok( err.message.includes(expectedErrorMessage), `expected error message to include "${expectedErrorMessage}"` From 71d0a3f9796c506a0f0bc282eba96339edca9a77 Mon Sep 17 00:00:00 2001 From: 0div Date: Thu, 1 May 2025 11:51:21 -0700 Subject: [PATCH 20/22] remove logs from test --- packages/js-sdk/tests/sandbox/files/list.test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/js-sdk/tests/sandbox/files/list.test.ts b/packages/js-sdk/tests/sandbox/files/list.test.ts index 14a77440fa..8d780a64c5 100644 --- a/packages/js-sdk/tests/sandbox/files/list.test.ts +++ b/packages/js-sdk/tests/sandbox/files/list.test.ts @@ -1,10 +1,9 @@ import { assert, onTestFinished } from 'vitest' -import { sandboxTest } from '../../setup.js' +import { sandboxTest, wait } from '../../setup.js' const parentDirName = 'test_directory' -/* sandboxTest('list directory', async ({ sandbox }) => { const homeDirName = '/home/user' await sandbox.files.makeDir(parentDirName) @@ -98,7 +97,6 @@ sandboxTest('list directory', async ({ sandbox }) => { sandbox.files.remove(parentDirName) }) }) -*/ sandboxTest('list directory with invalid depth', async ({ sandbox }) => { await sandbox.files.makeDir(parentDirName) @@ -108,7 +106,6 @@ sandboxTest('list directory with invalid depth', async ({ sandbox }) => { assert.fail('Expected error but none was thrown') } catch (err) { const expectedErrorMessage = 'depth should be at least one' - console.log(err.message) assert.ok( err.message.includes(expectedErrorMessage), `expected error message to include "${expectedErrorMessage}"` From e2aed0792ae61dcc0729aa998e51c2feb6037971 Mon Sep 17 00:00:00 2001 From: 0div Date: Thu, 1 May 2025 13:39:55 -0700 Subject: [PATCH 21/22] add type, name and path assertiosn in python-sdk tests --- .../sandbox_async/files/test_files_list.py | 116 +++++++++++++---- .../sandbox_sync/files/test_files_list.py | 118 ++++++++++++++---- 2 files changed, 181 insertions(+), 53 deletions(-) diff --git a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py index 04f593921c..39104a1aa1 100644 --- a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py +++ b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py @@ -4,6 +4,7 @@ async def test_list_directory(async_sandbox: AsyncSandbox): + home_dir_name = "/home/user" parent_dir_name = f"test_directory_{uuid.uuid4()}" await async_sandbox.files.make_dir(parent_dir_name) @@ -13,50 +14,111 @@ async def test_list_directory(async_sandbox: AsyncSandbox): await async_sandbox.files.make_dir(f"{parent_dir_name}/subdir1/subdir1_2") await async_sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_1") await async_sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_2") + await async_sandbox.files.write(f"{parent_dir_name}/file1.txt", "Hello, world!") test_cases = [ { "name": "default depth (1)", "depth": None, - "expected_len": 2, - "expected_files": [ - f"{parent_dir_name}/subdir1", - f"{parent_dir_name}/subdir2", + "expected_len": 3, + "expected_file_names": [ + "file1.txt", + "subdir1", + "subdir2", + ], + "expected_file_types": [ + FileType.FILE, + FileType.DIR, + FileType.DIR, + ], + "expected_file_paths": [ + f"{home_dir_name}/{parent_dir_name}/file1.txt", + f"{home_dir_name}/{parent_dir_name}/subdir1", + f"{home_dir_name}/{parent_dir_name}/subdir2", ], }, { "name": "explicit depth 1", "depth": 1, - "expected_len": 2, - "expected_files": [ - f"{parent_dir_name}/subdir1", - f"{parent_dir_name}/subdir2", + "expected_len": 3, + "expected_file_names": [ + "file1.txt", + "subdir1", + "subdir2", + ], + "expected_file_types": [ + FileType.FILE, + FileType.DIR, + FileType.DIR, + ], + "expected_file_paths": [ + f"{home_dir_name}/{parent_dir_name}/file1.txt", + f"{home_dir_name}/{parent_dir_name}/subdir1", + f"{home_dir_name}/{parent_dir_name}/subdir2", ], }, { "name": "explicit depth 2", "depth": 2, - "expected_len": 6, - "expected_files": [ - f"{parent_dir_name}/subdir1", - f"{parent_dir_name}/subdir1/subdir1_1", - f"{parent_dir_name}/subdir1/subdir1_2", - f"{parent_dir_name}/subdir2", - f"{parent_dir_name}/subdir2/subdir2_1", - f"{parent_dir_name}/subdir2/subdir2_2", + "expected_len": 7, + "expected_file_types": [ + FileType.FILE, + FileType.DIR, + FileType.DIR, + FileType.DIR, + FileType.DIR, + FileType.DIR, + FileType.DIR, + ], + "expected_file_names": [ + "file1.txt", + "subdir1", + "subdir1_1", + "subdir1_2", + "subdir2", + "subdir2_1", + "subdir2_2", + ], + "expected_file_paths": [ + f"{home_dir_name}/{parent_dir_name}/file1.txt", + f"{home_dir_name}/{parent_dir_name}/subdir1", + f"{home_dir_name}/{parent_dir_name}/subdir1/subdir1_1", + f"{home_dir_name}/{parent_dir_name}/subdir1/subdir1_2", + f"{home_dir_name}/{parent_dir_name}/subdir2", + f"{home_dir_name}/{parent_dir_name}/subdir2/subdir2_1", + f"{home_dir_name}/{parent_dir_name}/subdir2/subdir2_2", ], }, { "name": "explicit depth 3 (should be the same as depth 2)", "depth": 3, - "expected_len": 6, - "expected_files": [ - f"{parent_dir_name}/subdir1", - f"{parent_dir_name}/subdir1/subdir1_1", - f"{parent_dir_name}/subdir1/subdir1_2", - f"{parent_dir_name}/subdir2", - f"{parent_dir_name}/subdir2/subdir2_1", - f"{parent_dir_name}/subdir2/subdir2_2", + "expected_len": 7, + "expected_file_names": [ + "file1.txt", + "subdir1", + "subdir1_1", + "subdir1_2", + "subdir2", + "subdir2_1", + "subdir2_2", + ], + "expected_file_types": [ + FileType.FILE, + FileType.DIR, + FileType.DIR, + FileType.DIR, + FileType.DIR, + FileType.DIR, + FileType.DIR, + ], + "expected_file_paths": [ + f"{home_dir_name}/{parent_dir_name}/file1.txt", + f"{home_dir_name}/{parent_dir_name}/subdir1", + f"{home_dir_name}/{parent_dir_name}/subdir1/subdir1_1", + f"{home_dir_name}/{parent_dir_name}/subdir1/subdir1_2", + f"{home_dir_name}/{parent_dir_name}/subdir2", + f"{home_dir_name}/{parent_dir_name}/subdir2/subdir2_1", + f"{home_dir_name}/{parent_dir_name}/subdir2/subdir2_2", ], }, ] @@ -69,8 +131,10 @@ async def test_list_directory(async_sandbox: AsyncSandbox): assert len(files) == test_case["expected_len"] - for i, expected_name in enumerate(test_case["expected_files"]): - assert files[i].name == expected_name + for i in range(len(test_case["expected_file_names"])): + assert files[i].name == test_case["expected_file_names"][i] + assert files[i].path == test_case["expected_file_paths"][i] + assert files[i].type == test_case["expected_file_types"][i] await async_sandbox.files.remove(parent_dir_name) diff --git a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py index 1a1844f3c0..9e265ef34a 100644 --- a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py +++ b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py @@ -1,9 +1,10 @@ import uuid -from e2b import Sandbox +from e2b import Sandbox, FileType def test_list_directory(sandbox: Sandbox): + home_dir_name = "/home/user" parent_dir_name = f"test_directory_{uuid.uuid4()}" sandbox.files.make_dir(parent_dir_name) @@ -13,50 +14,111 @@ def test_list_directory(sandbox: Sandbox): sandbox.files.make_dir(f"{parent_dir_name}/subdir1/subdir1_2") sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_1") sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_2") + sandbox.files.write(f"{parent_dir_name}/file1.txt", "Hello, world!") test_cases = [ { "name": "default depth (1)", "depth": None, - "expected_len": 2, - "expected_files": [ - f"{parent_dir_name}/subdir1", - f"{parent_dir_name}/subdir2", + "expected_len": 3, + "expected_file_names": [ + "file1.txt", + "subdir1", + "subdir2", + ], + "expected_file_types": [ + FileType.FILE, + FileType.DIR, + FileType.DIR, + ], + "expected_file_paths": [ + f"{home_dir_name}/{parent_dir_name}/file1.txt", + f"{home_dir_name}/{parent_dir_name}/subdir1", + f"{home_dir_name}/{parent_dir_name}/subdir2", ], }, { "name": "explicit depth 1", "depth": 1, - "expected_len": 2, - "expected_files": [ - f"{parent_dir_name}/subdir1", - f"{parent_dir_name}/subdir2", + "expected_len": 3, + "expected_file_names": [ + "file1.txt", + "subdir1", + "subdir2", + ], + "expected_file_types": [ + FileType.FILE, + FileType.DIR, + FileType.DIR, + ], + "expected_file_paths": [ + f"{home_dir_name}/{parent_dir_name}/file1.txt", + f"{home_dir_name}/{parent_dir_name}/subdir1", + f"{home_dir_name}/{parent_dir_name}/subdir2", ], }, { "name": "explicit depth 2", "depth": 2, - "expected_len": 6, - "expected_files": [ - f"{parent_dir_name}/subdir1", - f"{parent_dir_name}/subdir1/subdir1_1", - f"{parent_dir_name}/subdir1/subdir1_2", - f"{parent_dir_name}/subdir2", - f"{parent_dir_name}/subdir2/subdir2_1", - f"{parent_dir_name}/subdir2/subdir2_2", + "expected_len": 7, + "expected_file_types": [ + FileType.FILE, + FileType.DIR, + FileType.DIR, + FileType.DIR, + FileType.DIR, + FileType.DIR, + FileType.DIR, + ], + "expected_file_names": [ + "file1.txt", + "subdir1", + "subdir1_1", + "subdir1_2", + "subdir2", + "subdir2_1", + "subdir2_2", + ], + "expected_file_paths": [ + f"{home_dir_name}/{parent_dir_name}/file1.txt", + f"{home_dir_name}/{parent_dir_name}/subdir1", + f"{home_dir_name}/{parent_dir_name}/subdir1/subdir1_1", + f"{home_dir_name}/{parent_dir_name}/subdir1/subdir1_2", + f"{home_dir_name}/{parent_dir_name}/subdir2", + f"{home_dir_name}/{parent_dir_name}/subdir2/subdir2_1", + f"{home_dir_name}/{parent_dir_name}/subdir2/subdir2_2", ], }, { "name": "explicit depth 3 (should be the same as depth 2)", "depth": 3, - "expected_len": 6, - "expected_files": [ - f"{parent_dir_name}/subdir1", - f"{parent_dir_name}/subdir1/subdir1_1", - f"{parent_dir_name}/subdir1/subdir1_2", - f"{parent_dir_name}/subdir2", - f"{parent_dir_name}/subdir2/subdir2_1", - f"{parent_dir_name}/subdir2/subdir2_2", + "expected_len": 7, + "expected_file_names": [ + "file1.txt", + "subdir1", + "subdir1_1", + "subdir1_2", + "subdir2", + "subdir2_1", + "subdir2_2", + ], + "expected_file_types": [ + FileType.FILE, + FileType.DIR, + FileType.DIR, + FileType.DIR, + FileType.DIR, + FileType.DIR, + FileType.DIR, + ], + "expected_file_paths": [ + f"{home_dir_name}/{parent_dir_name}/file1.txt", + f"{home_dir_name}/{parent_dir_name}/subdir1", + f"{home_dir_name}/{parent_dir_name}/subdir1/subdir1_1", + f"{home_dir_name}/{parent_dir_name}/subdir1/subdir1_2", + f"{home_dir_name}/{parent_dir_name}/subdir2", + f"{home_dir_name}/{parent_dir_name}/subdir2/subdir2_1", + f"{home_dir_name}/{parent_dir_name}/subdir2/subdir2_2", ], }, ] @@ -69,8 +131,10 @@ def test_list_directory(sandbox: Sandbox): assert len(files) == test_case["expected_len"] - for i, expected_name in enumerate(test_case["expected_files"]): - assert files[i].name == expected_name + for i in range(len(test_case["expected_file_names"])): + assert files[i].name == test_case["expected_file_names"][i] + assert files[i].path == test_case["expected_file_paths"][i] + assert files[i].type == test_case["expected_file_types"][i] sandbox.files.remove(parent_dir_name) From c70317de9e8b23772b1510c8f9b7f6723369d5fc Mon Sep 17 00:00:00 2001 From: 0div Date: Thu, 1 May 2025 14:25:42 -0700 Subject: [PATCH 22/22] fix js-sdk test 502 from cleanup function --- .../js-sdk/tests/sandbox/files/list.test.ts | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/packages/js-sdk/tests/sandbox/files/list.test.ts b/packages/js-sdk/tests/sandbox/files/list.test.ts index 8d780a64c5..8fc8c0e103 100644 --- a/packages/js-sdk/tests/sandbox/files/list.test.ts +++ b/packages/js-sdk/tests/sandbox/files/list.test.ts @@ -13,14 +13,17 @@ sandboxTest('list directory', async ({ sandbox }) => { await sandbox.files.makeDir(`${parentDirName}/subdir1/subdir1_2`) await sandbox.files.makeDir(`${parentDirName}/subdir2/subdir2_1`) await sandbox.files.makeDir(`${parentDirName}/subdir2/subdir2_2`) + await sandbox.files.write(`${parentDirName}/file1.txt`, 'Hello, world!') const testCases = [ { test_name: 'default depth (1)', depth: undefined, - expectedLen: 2, - expectedFileNames: ['subdir1', 'subdir2'], + expectedLen: 3, + expectedFileNames: ['file1.txt', 'subdir1', 'subdir2'], + expectedFileTypes: ['file', 'dir', 'dir'], expectedFilePaths: [ + `${homeDirName}/${parentDirName}/file1.txt`, `${homeDirName}/${parentDirName}/subdir1`, `${homeDirName}/${parentDirName}/subdir2`, ], @@ -28,9 +31,11 @@ sandboxTest('list directory', async ({ sandbox }) => { { test_name: 'explicit depth 1', depth: 1, - expectedLen: 2, - expectedFileNames: ['subdir1', 'subdir2'], + expectedLen: 3, + expectedFileNames: ['file1.txt', 'subdir1', 'subdir2'], + expectedFileTypes: ['file', 'dir', 'dir'], expectedFilePaths: [ + `${homeDirName}/${parentDirName}/file1.txt`, `${homeDirName}/${parentDirName}/subdir1`, `${homeDirName}/${parentDirName}/subdir2`, ], @@ -38,8 +43,10 @@ sandboxTest('list directory', async ({ sandbox }) => { { test_name: 'explicit depth 2', depth: 2, - expectedLen: 6, + expectedLen: 7, + expectedFileTypes: ['file', 'dir', 'dir', 'dir', 'dir', 'dir', 'dir'], expectedFileNames: [ + 'file1.txt', 'subdir1', 'subdir1_1', 'subdir1_2', @@ -48,6 +55,7 @@ sandboxTest('list directory', async ({ sandbox }) => { 'subdir2_2', ], expectedFilePaths: [ + `${homeDirName}/${parentDirName}/file1.txt`, `${homeDirName}/${parentDirName}/subdir1`, `${homeDirName}/${parentDirName}/subdir1/subdir1_1`, `${homeDirName}/${parentDirName}/subdir1/subdir1_2`, @@ -59,8 +67,10 @@ sandboxTest('list directory', async ({ sandbox }) => { { test_name: 'explicit depth 3 (should be the same as depth 2)', depth: 3, - expectedLen: 6, + expectedLen: 7, + expectedFileTypes: ['file', 'dir', 'dir', 'dir', 'dir', 'dir', 'dir'], expectedFileNames: [ + 'file1.txt', 'subdir1', 'subdir1_1', 'subdir1_2', @@ -69,6 +79,7 @@ sandboxTest('list directory', async ({ sandbox }) => { 'subdir2_2', ], expectedFilePaths: [ + `${homeDirName}/${parentDirName}/file1.txt`, `${homeDirName}/${parentDirName}/subdir1`, `${homeDirName}/${parentDirName}/subdir1/subdir1_1`, `${homeDirName}/${parentDirName}/subdir1/subdir1_2`, @@ -87,15 +98,11 @@ sandboxTest('list directory', async ({ sandbox }) => { assert.equal(files.length, testCase.expectedLen) for (let i = 0; i < testCase.expectedFilePaths.length; i++) { - assert.equal(files[i].type, 'dir') + assert.equal(files[i].type, testCase.expectedFileTypes[i]) assert.equal(files[i].name, testCase.expectedFileNames[i]) assert.equal(files[i].path, testCase.expectedFilePaths[i]) } } - - onTestFinished(() => { - sandbox.files.remove(parentDirName) - }) }) sandboxTest('list directory with invalid depth', async ({ sandbox }) => { @@ -111,8 +118,4 @@ sandboxTest('list directory with invalid depth', async ({ sandbox }) => { `expected error message to include "${expectedErrorMessage}"` ) } - - onTestFinished(() => { - sandbox.files.remove(parentDirName) - }) })