From 23b6e87f8550f5eb244736637117d7be3951a750 Mon Sep 17 00:00:00 2001 From: MagnusHJensen Date: Tue, 30 May 2023 18:17:40 +0200 Subject: [PATCH] support file size limit and mime types --- .gitignore | 4 +- CHANGELOG.md | 5 ++ infra/docker-compose.yml | 24 +++--- infra/postgres/dummy-data.sql | 4 +- infra/postgres/storage-schema.sql | 78 +++++++++---------- infra/storage/Dockerfile | 2 +- pom.xml | 2 +- src/main/java/io/supabase/StorageClient.java | 2 +- .../io/supabase/api/StorageBucketAPI.java | 10 ++- .../java/io/supabase/data/bucket/Bucket.java | 65 ++++++---------- .../data/bucket/BucketCreateOptions.java | 9 ++- .../supabase/data/bucket/BucketOptions.java | 18 ++++- .../data/bucket/BucketUpdateOptions.java | 8 +- .../data/bucket/CreateBucketResponse.java | 7 ++ src/main/java/io/supabase/utils/FileSize.java | 71 +++++++++++++++++ .../io/supabase/api/StorageBucketAPITest.java | 17 ++-- .../io/supabase/api/StorageFileAPITest.java | 3 +- .../java/io/supabase/utils/FileSizeTest.java | 21 +++++ 18 files changed, 239 insertions(+), 111 deletions(-) create mode 100644 src/main/java/io/supabase/utils/FileSize.java create mode 100644 src/test/java/io/supabase/utils/FileSizeTest.java diff --git a/.gitignore b/.gitignore index e57cb7e..7fb500b 100644 --- a/.gitignore +++ b/.gitignore @@ -136,4 +136,6 @@ buildNumber.properties # End of https://www.toptal.com/developers/gitignore/api/intellij+all,java,maven -infra/.env \ No newline at end of file +infra/.env + +TestApplication.java \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f74089..81aa44c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## v1.1.0 - [Supports v0.29.2](https://github.com/supabase/storage-api/releases/tag/v0.29.2) +- Added support for file size limit on bucket leve. +- Added utility class file size for parsing file sizes with `B, KB, MB, GB` in them +- Added support for allowed mime types on bucket leve, so you can now restrict uploads to buckets for a certain file type. + ## v1.0.1 - [Supports v0.28.2](https://github.com/supabase/storage-api/releases/tag/v0.28.2) - Added new `FileTransformOptions`. - Updated [javadocs](https://supabase-community.github.io/storage-java) diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index 6c7a37c..b00f7cc 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -10,8 +10,8 @@ services: KONG_DECLARATIVE_CONFIG: /var/lib/kong/kong.yml KONG_PLUGINS: request-transformer,cors,key-auth,http-log ports: - - "8000:8000/tcp" - - "8443:8443/tcp" + - 8000:8000/tcp + - 8443:8443/tcp rest: image: postgrest/postgrest:latest ports: @@ -38,14 +38,17 @@ services: ANON_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYxMzUzMTk4NSwiZXhwIjoxOTI5MTA3OTg1fQ.ReNhHIoXIOa-8tL1DO3e26mJmOTnYuvdgobwIYGzrLQ SERVICE_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIiwiaWF0IjoxNjEzNTMxOTg1LCJleHAiOjE5MjkxMDc5ODV9.FhK1kZdHmWdCIEZELt0QDCw6FIlCS8rVmp4RzaeI2LM PROJECT_REF: bjwdssmqcnupljrqypxz # can be any random string - REGION: eu-west-1 # region where your bucket is located POSTGREST_URL: http://rest:3000 - GLOBAL_S3_BUCKET: java-supa-storage-testing # name of s3 bucket where you want to store objects PGRST_JWT_SECRET: super-secret-jwt-token-with-at-least-32-characters-long - DATABASE_URL: postgres://postgres:postgres@db:5432/postgres - PGOPTIONS: "-c search_path=storage" + + REGION: eu-west-1 # region where your bucket is located + GLOBAL_S3_BUCKET: java-supa-storage-testing # name of s3 bucket where you want to store objects AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + + DATABASE_URL: postgres://postgres:postgres@db:5432/postgres + PGOPTIONS: "-c search_path=storage" + FILE_SIZE_LIMIT: 52428800 STORAGE_BACKEND: file FILE_STORAGE_BACKEND_PATH: /tmp/storage @@ -54,13 +57,12 @@ services: volumes: - assets-volume:/tmp/storage healthcheck: - test: [ 'CMD-SHELL', 'curl -f -LI http://localhost:5000/status' ] - + test: ['CMD-SHELL', 'curl -f -LI http://localhost:5000/status'] db: build: context: ./postgres ports: - - "5432:5432" + - 5432:5432 command: - postgres - -c @@ -79,9 +81,9 @@ services: imgproxy: image: darthsim/imgproxy ports: - - "50020:8080" + - 50020:8080 volumes: - - assets-volume:/tmp/storage + - assets-volume:/tmp/storage environment: - IMGPROXY_LOCAL_FILESYSTEM_ROOT=/ - IMGPROXY_USE_ETAG=true diff --git a/infra/postgres/dummy-data.sql b/infra/postgres/dummy-data.sql index 5d5feba..690ed45 100644 --- a/infra/postgres/dummy-data.sql +++ b/infra/postgres/dummy-data.sql @@ -51,4 +51,6 @@ CREATE POLICY authenticated_folder ON storage.objects for all USING (bucket_id=' -- allow CRUD access to a folder in bucket2 to its owners CREATE POLICY crud_owner_only ON storage.objects for all USING (bucket_id='bucket2' and (storage.foldername(name))[1] = 'only_owner' and owner = auth.uid()); -- allow CRUD access to bucket4 -CREATE POLICY open_all_update ON storage.objects for all WITH CHECK (bucket_id='bucket4'); \ No newline at end of file +CREATE POLICY open_all_update ON storage.objects for all WITH CHECK (bucket_id='bucket4'); + +CREATE POLICY crud_my_bucket ON storage.objects for all USING (bucket_id='my-private-bucket' and auth.uid()::text = '317eadce-631a-4429-a0bb-f19a7a517b4a'); \ No newline at end of file diff --git a/infra/postgres/storage-schema.sql b/infra/postgres/storage-schema.sql index 3db47ee..dd49a20 100644 --- a/infra/postgres/storage-schema.sql +++ b/infra/postgres/storage-schema.sql @@ -37,68 +37,68 @@ CREATE INDEX name_prefix_search ON storage.objects(name text_pattern_ops); ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY; CREATE OR REPLACE FUNCTION storage.foldername(name text) - RETURNS text[] - LANGUAGE plpgsql + RETURNS text[] + LANGUAGE plpgsql AS $function$ DECLARE -_parts text[]; + _parts text[]; BEGIN -select string_to_array(name, '/') into _parts; -return _parts[1:array_length(_parts,1)-1]; + select string_to_array(name, '/') into _parts; + return _parts[1:array_length(_parts,1)-1]; END $function$; CREATE OR REPLACE FUNCTION storage.filename(name text) - RETURNS text - LANGUAGE plpgsql + RETURNS text + LANGUAGE plpgsql AS $function$ DECLARE -_parts text[]; + _parts text[]; BEGIN -select string_to_array(name, '/') into _parts; -return _parts[array_length(_parts,1)]; + select string_to_array(name, '/') into _parts; + return _parts[array_length(_parts,1)]; END $function$; CREATE OR REPLACE FUNCTION storage.extension(name text) - RETURNS text - LANGUAGE plpgsql + RETURNS text + LANGUAGE plpgsql AS $function$ DECLARE -_parts text[]; -_filename text; + _parts text[]; + _filename text; BEGIN -select string_to_array(name, '/') into _parts; -select _parts[array_length(_parts,1)] into _filename; -return split_part(_filename, '.', 2); + select string_to_array(name, '/') into _parts; + select _parts[array_length(_parts,1)] into _filename; + return split_part(_filename, '.', 2); END $function$; CREATE OR REPLACE FUNCTION storage.search(prefix text, bucketname text, limits int DEFAULT 100, levels int DEFAULT 1, offsets int DEFAULT 0) - RETURNS TABLE ( - name text, - id uuid, - updated_at TIMESTAMPTZ, - created_at TIMESTAMPTZ, - last_accessed_at TIMESTAMPTZ, - metadata jsonb - ) - LANGUAGE plpgsql + RETURNS TABLE ( + name text, + id uuid, + updated_at TIMESTAMPTZ, + created_at TIMESTAMPTZ, + last_accessed_at TIMESTAMPTZ, + metadata jsonb + ) + LANGUAGE plpgsql AS $function$ BEGIN -return query - with files_folders as ( - select ((string_to_array(objects.name, '/'))[levels]) as folder - from objects - where objects.name ilike prefix || '%' - and bucket_id = bucketname - GROUP by folder - limit limits - offset offsets - ) -select files_folders.folder as name, objects.id, objects.updated_at, objects.created_at, objects.last_accessed_at, objects.metadata from files_folders - left join objects - on prefix || files_folders.folder = objects.name and objects.bucket_id=bucketname; + return query + with files_folders as ( + select ((string_to_array(objects.name, '/'))[levels]) as folder + from objects + where objects.name ilike prefix || '%' + and bucket_id = bucketname + GROUP by folder + limit limits + offset offsets + ) + select files_folders.folder as name, objects.id, objects.updated_at, objects.created_at, objects.last_accessed_at, objects.metadata from files_folders + left join objects + on prefix || files_folders.folder = objects.name and objects.bucket_id=bucketname; END $function$; diff --git a/infra/storage/Dockerfile b/infra/storage/Dockerfile index 7ffd6d7..8da2f90 100644 --- a/infra/storage/Dockerfile +++ b/infra/storage/Dockerfile @@ -1,3 +1,3 @@ -FROM supabase/storage-api:v0.28.0 +FROM supabase/storage-api:v0.29.0 RUN apk add curl --no-cache \ No newline at end of file diff --git a/pom.xml b/pom.xml index db79a56..6274619 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.supabase storage-java - 1.0.1 + 1.1.0 Storage Java An async client library for the Supabase Storage API in Java diff --git a/src/main/java/io/supabase/StorageClient.java b/src/main/java/io/supabase/StorageClient.java index ef2a408..e382cc3 100644 --- a/src/main/java/io/supabase/StorageClient.java +++ b/src/main/java/io/supabase/StorageClient.java @@ -15,7 +15,7 @@ public StorageClient(String apiKey, String url) { this(url, new HashMap<>() {{ put("Authorization", "Bearer " + apiKey); }}); - // Validate URL and throw if not a valid url. + //TODO: Validate URL and throw if not a valid url. } private StorageClient(String url, Map headers) { diff --git a/src/main/java/io/supabase/api/StorageBucketAPI.java b/src/main/java/io/supabase/api/StorageBucketAPI.java index fea1312..e958f63 100644 --- a/src/main/java/io/supabase/api/StorageBucketAPI.java +++ b/src/main/java/io/supabase/api/StorageBucketAPI.java @@ -1,11 +1,13 @@ package io.supabase.api; +import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import io.supabase.data.bucket.Bucket; import io.supabase.data.bucket.BucketCreateOptions; import io.supabase.data.bucket.BucketUpdateOptions; import io.supabase.data.bucket.CreateBucketResponse; +import io.supabase.utils.FileSize; import io.supabase.utils.MessageResponse; import io.supabase.utils.RestUtils; @@ -37,7 +39,7 @@ public StorageBucketAPI(String url, Map headers) { */ @Override public CompletableFuture createBucket(String bucketId) { - return createBucket(bucketId, new BucketCreateOptions(false)); + return createBucket(bucketId, new BucketCreateOptions(false, new FileSize(0), null)); } /** @@ -50,9 +52,15 @@ public CompletableFuture createBucket(String bucketId) { @Override public CompletableFuture createBucket(String bucketId, BucketCreateOptions options) { JsonObject body = new JsonObject(); + JsonArray allowedMimeTypes = new JsonArray(); + for (String mimeType : options.getAllowedMimeTypes()) { + allowedMimeTypes.add(mimeType); + } body.addProperty("name", bucketId); body.addProperty("id", bucketId); body.addProperty("public", options.isPublic()); + body.addProperty("file_size_limit", options.getFileSizeLimit().getFileSizeAsB()); + body.add("allowed_mime_types", allowedMimeTypes); return RestUtils.post(new TypeToken() { }, headers, url + "bucket", body); } diff --git a/src/main/java/io/supabase/data/bucket/Bucket.java b/src/main/java/io/supabase/data/bucket/Bucket.java index 1678b63..74d9928 100644 --- a/src/main/java/io/supabase/data/bucket/Bucket.java +++ b/src/main/java/io/supabase/data/bucket/Bucket.java @@ -1,49 +1,28 @@ package io.supabase.data.bucket; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; - -public class Bucket { - private final String id; - private final String name; - private final String owner; - @SerializedName("public") - private final boolean isBucketPublic; - @SerializedName("created_at") - private final String createdAt; - @SerializedName("updated_at") - private final String updatedAt; - - public Bucket(String id, String name, String owner, boolean isBucketPublic, String createdAt, String updatedAt) { - this.id = id; - this.name = name; - this.owner = owner; - this.isBucketPublic = isBucketPublic; - this.createdAt = createdAt; - this.updatedAt = updatedAt; - } - - public String getId() { - return id; - } - - public String getName() { - return name; - } - - public String getOwner() { - return owner; - } - - public boolean isBucketPublic() { - return isBucketPublic; - } - - public String getCreatedAt() { - return createdAt; - } - - public String getUpdatedAt() { - return updatedAt; +import io.supabase.utils.FileSize; + +import java.util.List; + +public record Bucket(String id, String name, String owner, + @SerializedName("public") boolean isBucketPublic, + @SerializedName("file_size_limit") @JsonAdapter(FileSize.class) FileSize fileSizeLimit, + @SerializedName("allowed_mime_types") List allowedMimeTypes, + @SerializedName("created_at") String createdAt, + @SerializedName("updated_at") String updatedAt) { + + @Override + public String toString() { + return "Bucket{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", owner='" + owner + '\'' + + ", isBucketPublic=" + isBucketPublic + + ", createdAt='" + createdAt + '\'' + + ", updatedAt='" + updatedAt + '\'' + + '}'; } } diff --git a/src/main/java/io/supabase/data/bucket/BucketCreateOptions.java b/src/main/java/io/supabase/data/bucket/BucketCreateOptions.java index 58b41d8..98b114e 100644 --- a/src/main/java/io/supabase/data/bucket/BucketCreateOptions.java +++ b/src/main/java/io/supabase/data/bucket/BucketCreateOptions.java @@ -1,8 +1,11 @@ package io.supabase.data.bucket; -public class BucketCreateOptions extends BucketOptions { +import io.supabase.utils.FileSize; + +import java.util.List; - public BucketCreateOptions(boolean isPublic) { - super(isPublic); +public class BucketCreateOptions extends BucketOptions { + public BucketCreateOptions(boolean isPublic, FileSize fileSizeLimit, List allowedMimeTypes) { + super(isPublic, fileSizeLimit, allowedMimeTypes == null ? List.of() : allowedMimeTypes); } } diff --git a/src/main/java/io/supabase/data/bucket/BucketOptions.java b/src/main/java/io/supabase/data/bucket/BucketOptions.java index 2991c18..00ded5c 100644 --- a/src/main/java/io/supabase/data/bucket/BucketOptions.java +++ b/src/main/java/io/supabase/data/bucket/BucketOptions.java @@ -1,13 +1,29 @@ package io.supabase.data.bucket; +import io.supabase.utils.FileSize; + +import java.util.List; + public class BucketOptions { private final boolean isPublic; + private final FileSize fileSizeLimit; + private final List allowedMimeTypes; - public BucketOptions(boolean isPublic) { + public BucketOptions(boolean isPublic, FileSize fileSizeLimit, List allowedMimeTypes) { this.isPublic = isPublic; + this.fileSizeLimit = fileSizeLimit; + this.allowedMimeTypes = List.copyOf(allowedMimeTypes); } public boolean isPublic() { return isPublic; } + + public FileSize getFileSizeLimit() { + return fileSizeLimit; + } + + public List getAllowedMimeTypes() { + return List.copyOf(allowedMimeTypes); + } } diff --git a/src/main/java/io/supabase/data/bucket/BucketUpdateOptions.java b/src/main/java/io/supabase/data/bucket/BucketUpdateOptions.java index 4b4f4c5..3d72365 100644 --- a/src/main/java/io/supabase/data/bucket/BucketUpdateOptions.java +++ b/src/main/java/io/supabase/data/bucket/BucketUpdateOptions.java @@ -1,7 +1,11 @@ package io.supabase.data.bucket; +import io.supabase.utils.FileSize; + +import java.util.List; + public class BucketUpdateOptions extends BucketOptions { - public BucketUpdateOptions(boolean isPublic) { - super(isPublic); + public BucketUpdateOptions(boolean isPublic, FileSize fileSizeLimit, List allowedMimeTypes) { + super(isPublic, fileSizeLimit, allowedMimeTypes == null ? List.of() : allowedMimeTypes); } } diff --git a/src/main/java/io/supabase/data/bucket/CreateBucketResponse.java b/src/main/java/io/supabase/data/bucket/CreateBucketResponse.java index 7c5c3b1..1c2b680 100644 --- a/src/main/java/io/supabase/data/bucket/CreateBucketResponse.java +++ b/src/main/java/io/supabase/data/bucket/CreateBucketResponse.java @@ -10,4 +10,11 @@ public CreateBucketResponse(String name) { public String getName() { return name; } + + @Override + public String toString() { + return "CreateBucketResponse{" + + "name='" + name + '\'' + + '}'; + } } diff --git a/src/main/java/io/supabase/utils/FileSize.java b/src/main/java/io/supabase/utils/FileSize.java new file mode 100644 index 0000000..1202450 --- /dev/null +++ b/src/main/java/io/supabase/utils/FileSize.java @@ -0,0 +1,71 @@ +package io.supabase.utils; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; + +import java.lang.reflect.Type; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class FileSize implements JsonDeserializer { + private long fileSize; + + public FileSize(String fileSizeWithUnit) { + Matcher matcher = Pattern.compile("(\\d+)(B|KB|MB|GB)", Pattern.CASE_INSENSITIVE).matcher(fileSizeWithUnit); + if (!matcher.matches()) throw new IllegalArgumentException("Invalid file size format"); + int value = Integer.parseInt(matcher.group(1)); + String unit = matcher.group(2).toUpperCase(); + + switch (unit) { + case "B" -> fileSize = value; + case "KB" -> fileSize = value * 1000L; + case "MB" -> fileSize = value * 1000L * 1000L; + case "GB" -> fileSize = value * 1000L * 1000L * 1000L; + } + } + + public FileSize(long fileSize) { + this.fileSize = fileSize; + } + + public String getFileSizeAsB() { + return fileSize + "B"; + } + + public String getFileSizeAsKB() { + return (fileSize / 1000L) + "KB"; + } + + public String getFileSizeAsMB() { + return (fileSize / 1000L / 1000L) + "MB"; + } + + public String getFileSizeAsGB() { + return (fileSize / 1000L / 1000L / 1000L) + "GB"; + } + + public long getFileSize() { + return fileSize; + } + + @Override + public FileSize deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + return new FileSize(jsonElement.getAsLong()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FileSize fileSize1 = (FileSize) o; + return fileSize == fileSize1.fileSize; + } + + @Override + public int hashCode() { + return Objects.hash(fileSize); + } +} diff --git a/src/test/java/io/supabase/api/StorageBucketAPITest.java b/src/test/java/io/supabase/api/StorageBucketAPITest.java index 44789af..0bba90d 100644 --- a/src/test/java/io/supabase/api/StorageBucketAPITest.java +++ b/src/test/java/io/supabase/api/StorageBucketAPITest.java @@ -7,6 +7,7 @@ import io.supabase.data.bucket.BucketUpdateOptions; import io.supabase.data.bucket.CreateBucketResponse; import io.supabase.errors.StorageException; +import io.supabase.utils.FileSize; import io.supabase.utils.MessageResponse; import org.junit.jupiter.api.*; @@ -60,24 +61,30 @@ public void getBucketThrowsIfNotFound() { @Test @Order(1) - public void createBucket() throws ExecutionException, InterruptedException { + public void createBucketSuccessfully() throws ExecutionException, InterruptedException { CreateBucketResponse response = client.createBucket(newBucketName).get(); assertEquals(response.getName(), newBucketName); + } @Test - public void createPublicBucket() throws ExecutionException, InterruptedException { + public void createPublicBucketSuccessfully() throws ExecutionException, InterruptedException { String bucketName = "my-new-public-bucket" + LocalDateTime.now(); - client.createBucket(bucketName, new BucketCreateOptions(true)).get(); + FileSize fileSizeLimit = new FileSize(0); + List mimeTypes = List.of("image/png", "image/jpeg"); + client.createBucket(bucketName, new BucketCreateOptions(true, fileSizeLimit, mimeTypes)).get(); Bucket actual = client.getBucket(bucketName).get(); assertTrue(actual.isBucketPublic()); + assertFalse(actual.allowedMimeTypes().isEmpty()); + assertTrue(actual.allowedMimeTypes().containsAll(mimeTypes)); + Assertions.assertEquals(fileSizeLimit.getFileSize(), actual.fileSizeLimit().getFileSize()); } @Test @Order(2) - public void updateBucket() throws ExecutionException, InterruptedException { - MessageResponse updateResponse = client.updateBucket(newBucketName, new BucketUpdateOptions(true)).get(); + public void updateBucketSuccessfully() throws ExecutionException, InterruptedException { + MessageResponse updateResponse = client.updateBucket(newBucketName, new BucketUpdateOptions(true, new FileSize(0), null)).get(); expect(updateResponse).toMatchSnapshot(); Bucket bucket = client.getBucket(newBucketName).get(); assertTrue(bucket.isBucketPublic()); diff --git a/src/test/java/io/supabase/api/StorageFileAPITest.java b/src/test/java/io/supabase/api/StorageFileAPITest.java index 0948fca..c1836f4 100644 --- a/src/test/java/io/supabase/api/StorageFileAPITest.java +++ b/src/test/java/io/supabase/api/StorageFileAPITest.java @@ -3,6 +3,7 @@ import io.supabase.StorageClient; import io.supabase.data.bucket.BucketCreateOptions; import io.supabase.data.file.*; +import io.supabase.utils.FileSize; import io.supabase.utils.MessageResponse; import io.supabase.utils.Utilities; import org.junit.jupiter.api.*; @@ -26,7 +27,7 @@ public class StorageFileAPITest { private static String newBucket(boolean isPublic) throws ExecutionException, InterruptedException { String bucketName = "bucket-" + LocalDateTime.now(); - client.createBucket(bucketName, new BucketCreateOptions(isPublic)).get(); + client.createBucket(bucketName, new BucketCreateOptions(isPublic, new FileSize(0), null)).get(); return bucketName; } diff --git a/src/test/java/io/supabase/utils/FileSizeTest.java b/src/test/java/io/supabase/utils/FileSizeTest.java new file mode 100644 index 0000000..25c97d9 --- /dev/null +++ b/src/test/java/io/supabase/utils/FileSizeTest.java @@ -0,0 +1,21 @@ +package io.supabase.utils; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class FileSizeTest { + + @Test + public void fileSizeParsesCorrectly() { + FileSize fileSizeInKb = new FileSize("1000B"); + FileSize fileSizeInGB = new FileSize("20GB"); + + assertEquals("1KB", fileSizeInKb.getFileSizeAsKB()); + assertEquals("0MB", fileSizeInKb.getFileSizeAsMB()); + + assertEquals("20000MB", fileSizeInGB.getFileSizeAsMB()); + assertEquals("20000000KB", fileSizeInGB.getFileSizeAsKB()); + assertEquals("20000000000B", fileSizeInGB.getFileSizeAsB()); + } +}