|
| 1 | +# Manual Testing Guide for `task-management` Edge Function |
| 2 | + |
| 3 | +This guide provides steps to manually test the `task-management` Supabase edge function after recent modifications. |
| 4 | + |
| 5 | +## 1. Prerequisites |
| 6 | + |
| 7 | +Before testing, ensure the following are set up in your Supabase project: |
| 8 | + |
| 9 | +### 1.1. `integration_keys` Table |
| 10 | + |
| 11 | +- Create a table named `integration_keys` with at least the following columns: |
| 12 | + |
| 13 | + - `key` (Type: `TEXT` or `VARCHAR`, Unique): Stores the integration key string. |
| 14 | + - `user_id` (Type: `UUID`, Foreign Key to `auth.users(id)`): The Supabase user ID this key is associated with. |
| 15 | + - `is_active` (Type: `BOOLEAN`): Whether the key is currently active. |
| 16 | + - `description` (Type: `TEXT`, Optional): A description for the key. |
| 17 | + |
| 18 | +- **Sample Data**: |
| 19 | + - **Valid & Active Key**: Insert a row for a user that exists in your `auth.users` table. |
| 20 | + - `key`: `test-integration-key-active` |
| 21 | + - `user_id`: (UUID of an existing user, e.g., `12345678-1234-1234-1234-1234567890ab`) |
| 22 | + - `is_active`: `true` |
| 23 | + - `description`: "Active key for testing" |
| 24 | + - **Valid & Inactive Key**: Insert another row. |
| 25 | + - `key`: `test-integration-key-inactive` |
| 26 | + - `user_id`: (Same or different UUID of an existing user) |
| 27 | + - `is_active`: `false` |
| 28 | + - `description`: "Inactive key for testing" |
| 29 | + |
| 30 | +### 1.2. `tasks` Table |
| 31 | + |
| 32 | +- Ensure you have a `tasks` table. The function attempts to insert into this table. |
| 33 | +- It should have at least these columns: |
| 34 | + - `title` (Type: `TEXT` or `VARCHAR`) |
| 35 | + - `description` (Type: `TEXT`, Optional) |
| 36 | + - `estimated_minute` (Type: `INTEGER`, Optional) |
| 37 | + - `task_date` (Type: `DATE`, Optional) |
| 38 | + - `user_id` (Type: `UUID`): This will store the `user_id` associated with the integration key. |
| 39 | + |
| 40 | +### 1.3. RLS (Row Level Security) on `tasks` Table |
| 41 | + |
| 42 | +- For the RLS test, ensure RLS is enabled for the `tasks` table. |
| 43 | +- Create a policy that restricts access based on `user_id`. A common policy would be: |
| 44 | + |
| 45 | + ```sql |
| 46 | + ALTER TABLE tasks ENABLE ROW LEVEL SECURITY; |
| 47 | + |
| 48 | + CREATE POLICY "Users can manage their own tasks" |
| 49 | + ON tasks |
| 50 | + FOR ALL |
| 51 | + USING (auth.uid() = user_id) |
| 52 | + WITH CHECK (auth.uid() = user_id); |
| 53 | + ``` |
| 54 | + |
| 55 | + _Note: This policy assumes `auth.uid()` will correctly resolve to the `user_id` set in the JWT generated by the function._ |
| 56 | + |
| 57 | +### 1.4. Environment Variables for the Edge Function |
| 58 | + |
| 59 | +Ensure the following environment variables are set for your `task-management` Supabase function (either locally via `.env` file if using `supabase start`, or in the Supabase project settings): |
| 60 | + |
| 61 | +- `SUPABASE_URL`: Your project's Supabase URL. |
| 62 | +- `SUPABASE_ANON_KEY`: Your project's anon key. |
| 63 | +- `SUPABASE_SERVICE_ROLE_KEY`: Your project's service role key. |
| 64 | +- `SUPABASE_JWT_SECRET`: A strong secret string used to sign the JWTs. This **must** be the same secret your Supabase project uses for its JWT verification. You can find this in your Supabase project's JWT settings. |
| 65 | + |
| 66 | +## 2. Testing Scenarios |
| 67 | + |
| 68 | +Use a tool like `curl` or Postman to make requests to the edge function. The default local URL is `http://127.0.0.1:54321/functions/v1/task-management`. |
| 69 | + |
| 70 | +### 2.1. Success Case |
| 71 | + |
| 72 | +- **Action**: Call the function with a valid `x-integration-id` (that is active) and valid JSON task data. |
| 73 | + ```bash |
| 74 | + curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/task-management' \ |
| 75 | + --header 'x-integration-id: test-integration-key-active' \ |
| 76 | + --header 'Content-Type: application/json' \ |
| 77 | + --data '{ |
| 78 | + "title": "My Test Task via Integration", |
| 79 | + "description": "This is a task created successfully.", |
| 80 | + "estimated_minute": 60 |
| 81 | + }' |
| 82 | + ``` |
| 83 | +- **Expected Outcome**: |
| 84 | + - HTTP Status Code: `200 OK` (or `201 Created` if you've customized it, but the current code returns 200). |
| 85 | + - Response Body: JSON similar to `{"message":"タスクが正常に作成されました","task":{...}}` where `task` contains the created task data, including its `id` and the correct `user_id`. |
| 86 | +- **Verification**: |
| 87 | + - Check the `tasks` table in your Supabase project. |
| 88 | + - A new task should be created with the details provided. |
| 89 | + - The `user_id` of the new task **must** match the `user_id` associated with `test-integration-key-active` in your `integration_keys` table. |
| 90 | + |
| 91 | +### 2.2. Error Cases |
| 92 | + |
| 93 | +#### 2.2.1. Missing `x-integration-id` Header |
| 94 | + |
| 95 | +- **Action**: Call the function without the `x-integration-id` header. |
| 96 | + ```bash |
| 97 | + curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/task-management' \ |
| 98 | + --header 'Content-Type: application/json' \ |
| 99 | + --data '{"title":"Task missing header"}' |
| 100 | + ``` |
| 101 | +- **Expected Outcome**: |
| 102 | + - HTTP Status Code: `400 Bad Request`. |
| 103 | + - Response Body: `{"error":"x-integration-id header is missing"}`. |
| 104 | + |
| 105 | +#### 2.2.2. Non-existent `x-integration-id` |
| 106 | + |
| 107 | +- **Action**: Call the function with an `x-integration-id` that is not in the `integration_keys` table. |
| 108 | + ```bash |
| 109 | + curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/task-management' \ |
| 110 | + --header 'x-integration-id: non-existent-key' \ |
| 111 | + --header 'Content-Type: application/json' \ |
| 112 | + --data '{"title":"Task with non-existent key"}' |
| 113 | + ``` |
| 114 | +- **Expected Outcome**: |
| 115 | + - HTTP Status Code: `404 Not Found`. |
| 116 | + - Response Body: `{"error":"Integration key not found."}`. |
| 117 | + |
| 118 | +#### 2.2.3. Inactive `x-integration-id` |
| 119 | + |
| 120 | +- **Action**: Call the function with an `x-integration-id` that is marked `is_active = false`. |
| 121 | + ```bash |
| 122 | + curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/task-management' \ |
| 123 | + --header 'x-integration-id: test-integration-key-inactive' \ |
| 124 | + --header 'Content-Type: application/json' \ |
| 125 | + --data '{"title":"Task with inactive key"}' |
| 126 | + ``` |
| 127 | +- **Expected Outcome**: |
| 128 | + - HTTP Status Code: `403 Forbidden`. |
| 129 | + - Response Body: `{"error":"Integration key is inactive."}`. |
| 130 | + |
| 131 | +#### 2.2.4. Invalid Task Data |
| 132 | + |
| 133 | +- **Action**: Call the function with invalid task data (e.g., missing title). |
| 134 | + ```bash |
| 135 | + curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/task-management' \ |
| 136 | + --header 'x-integration-id: test-integration-key-active' \ |
| 137 | + --header 'Content-Type: application/json' \ |
| 138 | + --data '{"description":"Task missing title"}' |
| 139 | + ``` |
| 140 | +- **Expected Outcome**: |
| 141 | + - HTTP Status Code: `400 Bad Request`. |
| 142 | + - Response Body: `{"error":"Invalid task data","details":["タイトルは必須です"]}`. |
| 143 | + |
| 144 | +#### 2.2.5. Missing Environment Variable (Conceptual) |
| 145 | + |
| 146 | +- **Action**: This is harder to test directly with `curl` without modifying the function's deployment environment. If you were to temporarily unset `SUPABASE_JWT_SECRET` (or any of the other required env vars like `SUPABASE_URL`, `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`) for the function and then call it: |
| 147 | + ```bash |
| 148 | + # Assuming SUPABASE_JWT_SECRET is unset for the function's environment |
| 149 | + curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/task-management' \ |
| 150 | + --header 'x-integration-id: test-integration-key-active' \ |
| 151 | + --header 'Content-Type: application/json' \ |
| 152 | + --data '{"title":"Test Env Var Missing"}' |
| 153 | + ``` |
| 154 | +- **Expected Outcome**: |
| 155 | + - HTTP Status Code: `500 Internal Server Error`. |
| 156 | + - Response Body: `{"error":"Server configuration error."}`. |
| 157 | + - Server-side logs for the function should indicate which variable was missing (e.g., "Missing one or more required environment variables..."). |
| 158 | + |
| 159 | +### 2.3. RLS Test (Conceptual & Practical) |
| 160 | + |
| 161 | +This tests if the JWT generated by the function correctly assumes the `user_id` for RLS policies on the `tasks` table. |
| 162 | + |
| 163 | +- **Prerequisite**: Ensure RLS policy from section 1.3 is active. |
| 164 | + |
| 165 | +- **Scenario 1: Task `user_id` matches JWT `sub` (Implicit)** |
| 166 | + |
| 167 | + - This is covered by the **Success Case (2.1)**. The `newTask` object in the function uses `user_id: taskData.user_id || actualUserId`. If `taskData.user_id` is NOT provided in the request body, `actualUserId` (from the integration key) is used. The JWT's `sub` claim is set to this `actualUserId`. RLS should allow the insert because `auth.uid()` (derived from the JWT `sub`) will match `newTask.user_id`. |
| 168 | + |
| 169 | +- **Scenario 2: Attempt to set `taskData.user_id` to a different user (if allowed by function logic)** |
| 170 | + |
| 171 | + - The current function logic: `user_id: taskData.user_id || actualUserId`. This means if `taskData.user_id` _is_ provided in the request, it will take precedence _before_ being inserted. The JWT, however, will still be generated for `actualUserId` (the one from the integration key). |
| 172 | + - **Action**: Send a request where `taskData.user_id` is a valid UUID of a _different_ user than the one associated with `test-integration-key-active`. |
| 173 | + ```bash |
| 174 | + # Assume 'other-user-uuid' is a valid UUID of a user who is NOT associated with 'test-integration-key-active' |
| 175 | + curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/task-management' \ |
| 176 | + --header 'x-integration-id: test-integration-key-active' \ |
| 177 | + --header 'Content-Type: application/json' \ |
| 178 | + --data '{ |
| 179 | + "title": "RLS Test Task - Mismatched UserID", |
| 180 | + "user_id": "other-user-uuid" |
| 181 | + }' |
| 182 | + ``` |
| 183 | + - **Expected Outcome**: |
| 184 | + - HTTP Status Code: `400 Bad Request` (This is what the current code returns on Supabase insert errors, which an RLS violation would cause). |
| 185 | + - Response Body: `{"error":"タスクの保存に失敗しました", "details":"..."}`. The details might contain "new row violates row-level security policy for table \"tasks\"" or similar. |
| 186 | + - **Verification**: |
| 187 | + |
| 188 | + - The task should NOT be created in the `tasks` table. |
| 189 | + - This confirms that even if the function logic _could_ try to set a different `user_id`, the RLS policy enforced by Supabase (using the JWT's `auth.uid()`) prevents it. |
| 190 | +
|
| 191 | + - **Note on `taskData.user_id`**: The current function allows `taskData.user_id` to be optionally passed. If the intention is that the `x-integration-id` _solely_ dictates the `user_id`, then the function logic should be changed to ignore any `user_id` in `taskData` and always use `actualUserId`. The RLS policy, however, provides a strong safeguard. |
| 192 | +
|
| 193 | +## 3. Cleanup |
| 194 | +
|
| 195 | +- Remember to remove or deactivate test data (integration keys, tasks) as needed after testing. |
| 196 | +- Revert any temporary changes to environment variables. |
| 197 | +
|
| 198 | +``` |
| 199 | +
|
| 200 | +``` |
0 commit comments