From 282e5635e39aa51b1a68b40f5e839a31f99874d5 Mon Sep 17 00:00:00 2001 From: sheida-shab Date: Wed, 4 Feb 2026 10:12:19 +0000 Subject: [PATCH 1/2] Add server and client validation for Bloom length (max 280 characters) --- backend/endpoints.py | 4 ++++ front-end/lib/api.mjs | 11 +++++++-- front-end/tests/bloom-length.spec.js | 34 ++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 front-end/tests/bloom-length.spec.js diff --git a/backend/endpoints.py b/backend/endpoints.py index 0e177a0..3b5f7fe 100644 --- a/backend/endpoints.py +++ b/backend/endpoints.py @@ -157,6 +157,10 @@ def send_bloom(): return type_check_error user = get_current_user() + # Check server-side length + content=request.json["content"] + if len(content)>280 : + return jsonify({"success": False, "error": "Bloom must be 280 characters or less"}), 400 blooms.add_bloom(sender=user, content=request.json["content"]) diff --git a/front-end/lib/api.mjs b/front-end/lib/api.mjs index f4b5339..7ada5c1 100644 --- a/front-end/lib/api.mjs +++ b/front-end/lib/api.mjs @@ -1,6 +1,7 @@ import {state} from "../index.mjs"; import {handleErrorDialog} from "../components/error.mjs"; + // === ABOUT THE STATE // state gives you these two functions only // updateState({stateKey: newValues}) @@ -194,10 +195,16 @@ async function getBloomsByHashtag(hashtag) { } async function postBloom(content) { + // Check client-side length first + if (content.length>280){ + handleErrorDialog(new Error("Bloom must be 280 characters or less")); + return { success: false }; + + } try { const data = await _apiRequest("/bloom", { method: "POST", - body: JSON.stringify({content}), + body: JSON.stringify({ content }), }); if (data.success) { @@ -208,7 +215,7 @@ async function postBloom(content) { return data; } catch (error) { // Error already handled by _apiRequest - return {success: false}; + return { success: false }; } } diff --git a/front-end/tests/bloom-length.spec.js b/front-end/tests/bloom-length.spec.js new file mode 100644 index 0000000..c4f71da --- /dev/null +++ b/front-end/tests/bloom-length.spec.js @@ -0,0 +1,34 @@ +import { test, expect } from "@playwright/test"; +import { loginAsSample } from "./test-utils"; + +test("server should reject blooms longer than 280 characters", async ({ + page, +}) => { + + await loginAsSample(page); + + const longBloom = "A".repeat(281); + + const result = await page.evaluate(async (content) => { + const res = await fetch("/bloom", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + credentials: "include", + body: JSON.stringify({ content }), + }); + + if (!res.ok) { + try { + return await res.json(); + } catch { + return { success: false }; + } + } + + return await res.json(); + }, longBloom); + + expect(result.success).toBe(false); +}); From 33e13ae31c33cb1a0df5385aef1476ea43d05996 Mon Sep 17 00:00:00 2001 From: sheida-shab Date: Fri, 6 Feb 2026 15:34:40 +0000 Subject: [PATCH 2/2] Add and use MAX_BLOOM_LENGTH constants for back-end and front-end --- backend/constants.py | 1 + backend/endpoints.py | 5 +++-- front-end/lib/api.mjs | 8 ++++++-- front-end/lib/constants.mjs | 1 + front-end/tests/bloom-length.spec.js | 12 +++++++----- 5 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 backend/constants.py create mode 100644 front-end/lib/constants.mjs diff --git a/backend/constants.py b/backend/constants.py new file mode 100644 index 0000000..b9dfac2 --- /dev/null +++ b/backend/constants.py @@ -0,0 +1 @@ +MAX_BLOOM_LENGTH = 280 \ No newline at end of file diff --git a/backend/endpoints.py b/backend/endpoints.py index 3b5f7fe..892478e 100644 --- a/backend/endpoints.py +++ b/backend/endpoints.py @@ -1,3 +1,4 @@ +from constants import MAX_BLOOM_LENGTH from typing import Dict, Union from data import blooms from data.follows import follow, get_followed_usernames, get_inverse_followed_usernames @@ -159,8 +160,8 @@ def send_bloom(): user = get_current_user() # Check server-side length content=request.json["content"] - if len(content)>280 : - return jsonify({"success": False, "error": "Bloom must be 280 characters or less"}), 400 + if len(content)>MAX_BLOOM_LENGTH : + return jsonify({"success": False, "error": f"Bloom must be {MAX_BLOOM_LENGTH} characters or less"}), 400 blooms.add_bloom(sender=user, content=request.json["content"]) diff --git a/front-end/lib/api.mjs b/front-end/lib/api.mjs index 7ada5c1..5018ecf 100644 --- a/front-end/lib/api.mjs +++ b/front-end/lib/api.mjs @@ -1,5 +1,7 @@ import {state} from "../index.mjs"; import {handleErrorDialog} from "../components/error.mjs"; +import { MAX_BLOOM_LENGTH } from "./constants.mjs"; + // === ABOUT THE STATE @@ -196,8 +198,10 @@ async function getBloomsByHashtag(hashtag) { async function postBloom(content) { // Check client-side length first - if (content.length>280){ - handleErrorDialog(new Error("Bloom must be 280 characters or less")); + if (content.length>MAX_BLOOM_LENGTH){ + handleErrorDialog( + new Error(`Bloom must be ${MAX_BLOOM_LENGTH} characters or less`), + ); return { success: false }; } diff --git a/front-end/lib/constants.mjs b/front-end/lib/constants.mjs new file mode 100644 index 0000000..dc4099f --- /dev/null +++ b/front-end/lib/constants.mjs @@ -0,0 +1 @@ +export const MAX_BLOOM_LENGTH = 280; diff --git a/front-end/tests/bloom-length.spec.js b/front-end/tests/bloom-length.spec.js index c4f71da..73442a9 100644 --- a/front-end/tests/bloom-length.spec.js +++ b/front-end/tests/bloom-length.spec.js @@ -1,13 +1,15 @@ import { test, expect } from "@playwright/test"; import { loginAsSample } from "./test-utils"; +import { MAX_BLOOM_LENGTH } from "../lib/constants.mjs"; -test("server should reject blooms longer than 280 characters", async ({ + + +test(`server should reject blooms longer than ${MAX_BLOOM_LENGTH} characters`, async ({ page, }) => { - await loginAsSample(page); - const longBloom = "A".repeat(281); + const longBloom = "A".repeat(MAX_BLOOM_LENGTH + 1); const result = await page.evaluate(async (content) => { const res = await fetch("/bloom", { @@ -15,11 +17,11 @@ test("server should reject blooms longer than 280 characters", async ({ headers: { "Content-Type": "application/json", }, - credentials: "include", + credentials: "include", body: JSON.stringify({ content }), }); - if (!res.ok) { + if (!res.ok) { try { return await res.json(); } catch {