diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index add4a56..3178198 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -8,7 +8,6 @@ on: env: NODE_VERSION: 22 - WRANGLER_VERSION: 3.99.0 jobs: deploy: runs-on: ubuntu-latest @@ -36,5 +35,4 @@ jobs: accountId: ${{ secrets.CF_ACCOUNT_ID }} command: "deploy --env production" environment: "production" - wranglerVersion: ${{ env.WRANGLER_VERSION }} diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 8a2a3a9..efe7172 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -30,5 +30,4 @@ jobs: - run: npm run build - run: npm run lint:js - run: npm run check-types - - run: cargo clippy --all-targets --all-features - run: npm run test diff --git a/Cargo.lock b/Cargo.lock index 2bfd003..cac76d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,12 +98,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -125,7 +119,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen", ] @@ -141,7 +135,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -186,19 +180,13 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "futures" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" - [[package]] name = "getrandom" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -209,7 +197,7 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -296,14 +284,13 @@ dependencies = [ name = "mcavatar" version = "0.1.0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "clippy", "console_error_panic_hook", "image", "imageproc", "js-sys", "wasm-bindgen", - "wasm-bindgen-test", ] [[package]] @@ -448,15 +435,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid", -] - [[package]] name = "proc-macro2" version = "1.0.92" @@ -466,22 +444,13 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", -] - [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ - "proc-macro2 1.0.92", + "proc-macro2", ] [[package]] @@ -568,12 +537,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "simba" version = "0.8.1" @@ -599,8 +562,8 @@ version = "2.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.38", + "proc-macro2", + "quote", "unicode-ident", ] @@ -633,12 +596,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -657,7 +614,7 @@ version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "once_cell", "wasm-bindgen-macro", ] @@ -670,32 +627,19 @@ checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "proc-macro2 1.0.92", - "quote 1.0.38", + "proc-macro2", + "quote", "syn", "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83420b37346c311b9ed822af41ec2e82839bfe99867ec6c54e2da43b7538771c" -dependencies = [ - "cfg-if 0.1.10", - "futures", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ - "quote 1.0.38", + "quote", "wasm-bindgen-macro-support", ] @@ -705,8 +649,8 @@ version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.38", + "proc-macro2", + "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -718,41 +662,6 @@ version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" -[[package]] -name = "wasm-bindgen-test" -version = "0.2.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d9693b63a742d481c7f80587e057920e568317b2806988c59cd71618bc26c1" -dependencies = [ - "console_error_panic_hook", - "futures", - "js-sys", - "scoped-tls", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test-macro", -] - -[[package]] -name = "wasm-bindgen-test-macro" -version = "0.2.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0789dac148a8840bbcf9efe13905463b733fa96543bfbf263790535c11af7ba5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", -] - -[[package]] -name = "web-sys" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "wide" version = "0.7.30" @@ -801,7 +710,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.38", + "proc-macro2", + "quote", "syn", ] diff --git a/Cargo.toml b/Cargo.toml index c85dcf6..05d0284 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ imageproc = { version = "0.25.0", default-features = false } # logging them with `console.error`. This is great for development, but requires # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for # code size when deploying. -console_error_panic_hook = { version = "0.1.6", optional = true } +console_error_panic_hook = { version = "0.1.7", optional = true } [dependencies.image] # Make `image` more lightweight. We don't need every image format under the sun, @@ -28,7 +28,6 @@ default-features = false features = ["png"] [dev-dependencies] -wasm-bindgen-test = "0.2" clippy = "0.0.302" [profile.release] diff --git a/package.json b/package.json index dc59c31..30a9c3d 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,9 @@ "lint": "npm run lint:js && npm run lint:rs && npm run check-types", "lint:js": "eslint \"**/*.{js,mjs,cjs,ts}\" \"**/*.json\"", "lint:js:fix": "npm run lint:js -- --fix", - "lint:rs": "cargo fmt --all --check", - "lint:rs:fix": "cargo fmt --all", - "test": "vitest --run", + "lint:rs": "cargo fmt --all --check && cargo clippy --all-targets --all-features", + "lint:rs:fix": "cargo fmt --all && cargo clippy --all-targets --all-features --fix", + "test": "vitest --run && cargo test", "test:dev": "vitest --watch" }, "devDependencies": { diff --git a/src/rust/lib.rs b/src/rust/lib.rs index 4403b6f..93dd24f 100644 --- a/src/rust/lib.rs +++ b/src/rust/lib.rs @@ -11,6 +11,7 @@ use skin::*; use std::io::Cursor; use wasm_bindgen::prelude::*; +#[derive(Debug, PartialEq)] enum RenderType { Avatar, Helm, @@ -104,3 +105,61 @@ pub fn get_rendered_image( Err(_err) => Err(js_sys::Error::new("Couldn't load skin.").into()), } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_what_to_render_type_avatar() { + assert_eq!( + what_to_render_type("avatar".to_string()), + Some(RenderType::Avatar) + ); + } + + #[test] + fn test_what_to_render_type_helm() { + assert_eq!( + what_to_render_type("helm".to_string()), + Some(RenderType::Helm) + ); + } + + #[test] + fn test_what_to_render_type_cube() { + assert_eq!( + what_to_render_type("cube".to_string()), + Some(RenderType::Cube) + ); + } + + #[test] + fn test_what_to_render_type_body() { + assert_eq!( + what_to_render_type("body".to_string()), + Some(RenderType::Body) + ); + } + + #[test] + fn test_what_to_render_type_bust() { + assert_eq!( + what_to_render_type("bust".to_string()), + Some(RenderType::Bust) + ); + } + + #[test] + fn test_what_to_render_type_cape() { + assert_eq!( + what_to_render_type("cape".to_string()), + Some(RenderType::Cape) + ); + } + + #[test] + fn test_what_to_render_type_invalid() { + assert_eq!(what_to_render_type("invalid".to_string()), None); + } +} diff --git a/src/rust/utils.rs b/src/rust/utils.rs index 22d56e0..d8e7bf5 100644 --- a/src/rust/utils.rs +++ b/src/rust/utils.rs @@ -83,3 +83,124 @@ pub(crate) fn fast_overlay(bottom: &mut DynamicImage, top: &DynamicImage, x: u32 } } } + +#[cfg(test)] +mod tests { + use super::*; + use image::{Rgba, RgbaImage}; + + #[test] + fn test_is_image_region_transparent_to_minecraft_transparent() { + let mut img = RgbaImage::new(10, 10); + img.put_pixel(5, 5, Rgba([0, 0, 0, 127])); // Transparent pixel + let img = DynamicImage::ImageRgba8(img); + assert!(is_image_region_transparent_to_minecraft(&img, 0, 0, 10, 10)); + } + + #[test] + fn test_is_image_region_transparent_to_minecraft_opaque() { + let mut img = RgbaImage::new(10, 10); + for y in 0..10 { + for x in 0..10 { + img.put_pixel(x, y, Rgba([0, 0, 0, 255])); // Fully opaque + } + } + let img = DynamicImage::ImageRgba8(img); + assert!(!is_image_region_transparent_to_minecraft( + &img, 0, 0, 10, 10 + )); + } + + #[test] + fn test_apply_minecraft_transparency() { + let mut img = RgbaImage::new(10, 10); + img.put_pixel(5, 5, Rgba([0, 0, 0, 127])); // Transparent pixel + let mut img = DynamicImage::ImageRgba8(img); + apply_minecraft_transparency(&mut img); + assert_eq!(img.get_pixel(5, 5)[3], 127); // Should remain transparent + assert_eq!(img.get_pixel(0, 0)[3], 0); // Should be made transparent + } + + #[test] + fn test_apply_minecraft_transparency_region() { + let mut img = RgbaImage::new(10, 10); + img.put_pixel(5, 5, Rgba([0, 0, 0, 127])); // Transparent pixel + let mut img = DynamicImage::ImageRgba8(img); + apply_minecraft_transparency_region(&mut img, 0, 0, 10, 10); + assert_eq!(img.get_pixel(5, 5)[3], 127); // Should remain transparent + assert_eq!(img.get_pixel(0, 0)[3], 0); // Should be made transparent + } + + #[test] + fn test_apply_minecraft_transparency_fully_transparent() { + let mut img = RgbaImage::new(10, 10); + for y in 0..10 { + for x in 0..10 { + img.put_pixel(x, y, Rgba([0, 0, 0, 0])); // Fully transparent + } + } + let mut img = DynamicImage::ImageRgba8(img); + apply_minecraft_transparency(&mut img); + assert_eq!(img.get_pixel(0, 0)[3], 0); // Should remain transparent + } + + #[test] + fn test_apply_minecraft_transparency_region_fully_transparent() { + let mut img = RgbaImage::new(10, 10); + for y in 0..10 { + for x in 0..10 { + img.put_pixel(x, y, Rgba([0, 0, 0, 0])); // Fully transparent + } + } + let mut img = DynamicImage::ImageRgba8(img); + apply_minecraft_transparency_region(&mut img, 0, 0, 10, 10); + assert_eq!(img.get_pixel(0, 0)[3], 0); // Should remain transparent + } + + #[test] + fn test_fast_overlay_within_bounds() { + let bottom = RgbaImage::new(10, 10); + let mut top = RgbaImage::new(5, 5); + for y in 0..5 { + for x in 0..5 { + top.put_pixel(x, y, Rgba([255, 0, 0, 255])); // Red opaque pixel + } + } + let mut bottom = DynamicImage::ImageRgba8(bottom); + let top = DynamicImage::ImageRgba8(top); + fast_overlay(&mut bottom, &top, 2, 2); + assert_eq!(bottom.get_pixel(2, 2), Rgba([255, 0, 0, 255])); // Should be red + } + + #[test] + fn test_fast_overlay_out_of_bounds() { + let bottom = RgbaImage::new(10, 10); + let mut top = RgbaImage::new(5, 5); + for y in 0..5 { + for x in 0..5 { + top.put_pixel(x, y, Rgba([255, 0, 0, 255])); // Red opaque pixel + } + } + let mut bottom = DynamicImage::ImageRgba8(bottom); + let top = DynamicImage::ImageRgba8(top); + fast_overlay(&mut bottom, &top, 8, 8); + assert_eq!(bottom.get_pixel(8, 8), Rgba([255, 0, 0, 255])); // Should be red + assert_eq!(bottom.get_pixel(9, 9), Rgba([255, 0, 0, 255])); // Should be red + assert_eq!(bottom.get_pixel(7, 7), Rgba([0, 0, 0, 0])); // Should be unchanged + } + + #[test] + fn test_fast_overlay_fully_transparent_top() { + let bottom = RgbaImage::new(10, 10); + let mut top = RgbaImage::new(5, 5); + for y in 0..5 { + for x in 0..5 { + top.put_pixel(x, y, Rgba([255, 0, 0, 0])); // Fully transparent pixel + } + } + let mut bottom = DynamicImage::ImageRgba8(bottom); + let top = DynamicImage::ImageRgba8(top); + fast_overlay(&mut bottom, &top, 2, 2); + assert_eq!(bottom.get_pixel(2, 2), Rgba([0, 0, 0, 0])); // Should remain unchanged + } +} diff --git a/src/worker/index.ts b/src/worker/index.ts index c875d20..d814adc 100644 --- a/src/worker/index.ts +++ b/src/worker/index.ts @@ -1,5 +1,4 @@ import { EMPTY } from './data'; -import PromiseGatherer from './promise_gather'; import { RequestedKind, interpretRequest } from './request'; import { DirectMojangApiService } from './services/mojang/api'; import MojangRequestService from './services/mojang/service'; @@ -76,10 +75,10 @@ async function renderImage(skin: Response, request: CraftheadRequest): Promise { +async function processRequest(skinService: MojangRequestService, interpreted: CraftheadRequest): Promise { switch (interpreted.requested) { case RequestedKind.Profile: { - const lookup = await skinService.fetchProfile(interpreted, gatherer); + const lookup = await skinService.fetchProfile(interpreted); if (!lookup.result) { return new Response(JSON.stringify({ error: 'User does not exist' }), { status: 404, @@ -99,14 +98,14 @@ async function processRequest(skinService: MojangRequestService, interpreted: Cr case RequestedKind.Cube: case RequestedKind.Body: case RequestedKind.Bust: { - const skin = await skinService.retrieveSkin(interpreted, gatherer); + const skin = await skinService.retrieveSkin(interpreted); return renderImage(skin, interpreted); } case RequestedKind.Skin: { - return skinService.retrieveSkin(interpreted, gatherer); + return skinService.retrieveSkin(interpreted); } case RequestedKind.Cape: { - const cape = await skinService.retrieveCape(interpreted, gatherer); + const cape = await skinService.retrieveCape(interpreted); if (cape.status === 404) { return new Response(EMPTY, { status: 404, @@ -166,17 +165,14 @@ async function handleRequest(request: Request, env: Env, ctx: ExecutionContext) // The item is not in the Cloudflare datacenter's cache. We need to process the request further. //console.log('Request not satisfied from cache.'); - const gatherer = new PromiseGatherer(); - const skinService = new MojangRequestService(new DirectMojangApiService(env, request)); - response = await processRequest(skinService, interpreted, gatherer); + response = await processRequest(skinService, interpreted); if (response.ok) { const cacheResponse = response.clone(); cacheResponse.headers.set('Content-Type', interpreted.requested === RequestedKind.Profile ? 'application/json' : 'image/png'); cacheResponse.headers.set('Cache-Control', 'max-age=14400'); - gatherer.push(caches.default.put(new Request(cacheKey), cacheResponse)); + ctx.waitUntil(caches.default.put(new Request(cacheKey), cacheResponse)); } - await gatherer.all(); } const headers = decorateHeaders(interpreted, response.headers, hitCache); writeDataPoint(env.CRAFTHEAD_ANALYTICS, request, { diff --git a/src/worker/promise_gather.ts b/src/worker/promise_gather.ts deleted file mode 100644 index 44c4769..0000000 --- a/src/worker/promise_gather.ts +++ /dev/null @@ -1,17 +0,0 @@ -// PromiseGatherer is intended to collect promises we don't need to await on right away, -// and defer them to be run after the request ends. -export default class PromiseGatherer { - promises: Promise[]; - - constructor() { - this.promises = []; - } - - push(promise: Promise) { - this.promises.push(promise); - } - - all(): Promise { - return Promise.all(this.promises); - } -} diff --git a/src/worker/request.ts b/src/worker/request.ts index 567a4ed..a1da15a 100644 --- a/src/worker/request.ts +++ b/src/worker/request.ts @@ -106,6 +106,9 @@ export function interpretRequest(request: Request): CraftheadRequest | null { // eslint-disable-next-line prefer-const let [requestedKindString, identity, sizeString] = url.pathname.split('/').slice(sliceAmt); + if (!identity) { + return null; + } let size = Number.parseInt(sizeString, 10); if (!size) { diff --git a/src/worker/services/mojang/api.ts b/src/worker/services/mojang/api.ts index 887b4d9..e2b37ce 100644 --- a/src/worker/services/mojang/api.ts +++ b/src/worker/services/mojang/api.ts @@ -1,4 +1,3 @@ -import type PromiseGatherer from '../../promise_gather'; import type { Env } from '../../types'; import type { CacheComputeResult } from '../../util/cache-helper'; @@ -41,9 +40,9 @@ export interface PlayerDBProfile { } export interface MojangApiService { - lookupUsername(usernames: string, gatherer: PromiseGatherer | null): Promise; + lookupUsername(usernames: string): Promise; - fetchProfile(id: string, gatherer: PromiseGatherer | null): Promise>; + fetchProfile(id: string): Promise>; } const PlayerDBHeaders = { @@ -58,7 +57,7 @@ export class DirectMojangApiService implements MojangApiService { this.env = env; this.request = request; } - async lookupUsername(username: string, gatherer: PromiseGatherer | null): Promise { + async lookupUsername(username: string): Promise { let lookupResponse: Response; if (this.env.PLAYERDB) { const request = new Request(`https://playerdb.co/api/player/minecraft/${username}`, { @@ -98,7 +97,7 @@ export class DirectMojangApiService implements MojangApiService { } } - async fetchProfile(id: string, gatherer: PromiseGatherer | null): Promise> { + async fetchProfile(id: string): Promise> { let profileResponse: Response; if (this.env.PLAYERDB) { const request = new Request(`https://playerdb.co/api/player/minecraft/${id}`, { diff --git a/src/worker/services/mojang/service.ts b/src/worker/services/mojang/service.ts index 2c63de7..45ac288 100644 --- a/src/worker/services/mojang/service.ts +++ b/src/worker/services/mojang/service.ts @@ -9,7 +9,6 @@ import { } from '../../util/uuid'; import type { MojangApiService, MojangProfile, MojangProfileProperty } from './api'; -import type PromiseGatherer from '../../promise_gather'; import type { CraftheadRequest } from '../../request'; import type { CacheComputeResult } from '../../util/cache-helper'; @@ -43,17 +42,15 @@ export default class MojangRequestService { /** * Normalizes the incoming request, such that we only work with UUIDs. A new request bearing an UUID is returned. - * @param request the incoming request - * @param gatherer any promise gatherer */ - async normalizeRequest(request: CraftheadRequest, gatherer: PromiseGatherer): Promise { + async normalizeRequest(request: CraftheadRequest): Promise { if (request.identityType === IdentityKind.Uuid || request.identityType === IdentityKind.TextureID) { return request; } const normalized: CraftheadRequest = { ...request, identityType: IdentityKind.Uuid }; - const profileLookup = await this.mojangApi.lookupUsername(request.identity, gatherer); + const profileLookup = await this.mojangApi.lookupUsername(request.identity); if (profileLookup) { normalized.identity = profileLookup.id; } else { @@ -66,7 +63,7 @@ export default class MojangRequestService { /** * Fetches a texture directly from the Mojang servers. Assumes the request has been normalized already. */ - private async retrieveTextureDirect(request: CraftheadRequest, gatherer: PromiseGatherer, kind: TextureKind): Promise { + private async retrieveTextureDirect(request: CraftheadRequest, kind: TextureKind): Promise { if (request.identityType === IdentityKind.TextureID) { const textureResponse = await MojangRequestService.fetchTextureFromId(request.identity); return { @@ -75,7 +72,7 @@ export default class MojangRequestService { } const rawUuid = fromHex(request.identity); if (uuidVersion(rawUuid) === 4) { - const lookup = await this.mojangApi.fetchProfile(request.identity, gatherer); + const lookup = await this.mojangApi.fetchProfile(request.identity); if (lookup.result) { const textureResponse = await MojangRequestService.fetchTextureFromProfile(lookup.result, kind); if (textureResponse) { @@ -134,14 +131,14 @@ export default class MojangRequestService { }); } - async retrieveSkin(request: CraftheadRequest, gatherer: PromiseGatherer): Promise { + async retrieveSkin(request: CraftheadRequest): Promise { if (request.identity === 'char' || request.identity === 'MHF_Steve') { // These are special-cased by Minotar. return new Response(STEVE_SKIN); } - const normalized = await this.normalizeRequest(request, gatherer); - const skin = await this.retrieveTextureDirect(normalized, gatherer, TextureKind.SKIN); + const normalized = await this.normalizeRequest(request); + const skin = await this.retrieveTextureDirect(normalized, TextureKind.SKIN); if (skin.texture.status === 404) { // Offline mode ID (usually when we have a username and the username isn't valid) const rawUuid = fromHex(normalized.identity); @@ -165,9 +162,9 @@ export default class MojangRequestService { return skin.texture; } - async retrieveCape(request: CraftheadRequest, gatherer: PromiseGatherer): Promise { - const normalized = await this.normalizeRequest(request, gatherer); - const cape = await this.retrieveTextureDirect(normalized, gatherer, TextureKind.CAPE); + async retrieveCape(request: CraftheadRequest): Promise { + const normalized = await this.normalizeRequest(request); + const cape = await this.retrieveTextureDirect(normalized, TextureKind.CAPE); if (cape.texture.status === 404) { return new Response(EMPTY, { status: 404, @@ -240,15 +237,15 @@ export default class MojangRequestService { return { texture: textureResponse, model: 'default' }; } - async fetchProfile(request: CraftheadRequest, gatherer: PromiseGatherer): Promise> { - const normalized = await this.normalizeRequest(request, gatherer); + async fetchProfile(request: CraftheadRequest): Promise> { + const normalized = await this.normalizeRequest(request); if (!normalized.identity || uuidVersion(fromHex(normalized.identity)) === 3) { return { result: null, source: 'mojang', }; } - return this.mojangApi.fetchProfile(normalized.identity, gatherer); + return this.mojangApi.fetchProfile(normalized.identity); } private static extractDataFromTexturesProperty(property: MojangProfileProperty | undefined): MojangTextureData | undefined { diff --git a/test/wasm.test.ts b/test/wasm.test.ts new file mode 100644 index 0000000..8deaa12 --- /dev/null +++ b/test/wasm.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'vitest'; + +import mcavatar from '../pkg/mcavatar'; + +describe('wasm', async () => { + it('should render a skin', async () => { + const skinRes = await fetch('https://textures.minecraft.net/texture/9d2e80355eed693e3f0485893ef04ff6a507f3aab33f2bedb48cef56e30f67d0'); + const skinArrayBuffer = await skinRes.arrayBuffer(); + const skinBuf = new Uint8Array(skinArrayBuffer); + const image = mcavatar.get_rendered_image(skinBuf, 64, 'avatar', false, false); + expect(image).toBeDefined(); + }); + + it('should throw with bad input', async () => { + const skinBuf = new Uint8Array(0); + expect(() => mcavatar.get_rendered_image(skinBuf, 64, 'avatar', false, false)).toThrowError(); + }); +}); diff --git a/test/worker.test.ts b/test/worker.test.ts index d763e67..f892b26 100644 --- a/test/worker.test.ts +++ b/test/worker.test.ts @@ -94,7 +94,7 @@ describe('worker requests', () => { expect(await response.headers.get('content-type')).toContain('image/png'); }); - it('responds with image for body on Id', async () => { + it('responds with image for body on ID', async () => { const request = new IncomingRequest('http://crafthead.net/body/ef6134805b6244e4a4467fbe85d65513'); const ctx = createExecutionContext(); const response = await worker.fetch(request, env, ctx);