Preface

In this article, I will demonstrate how to reliably extend the functionality of the existing code using the power and versatility of OpenAI. We will explore practical approaches that ensure a robust enhancement process, allowing developers to build upon tested solutions with confidence. Throughout the article, I will provide examples and best practices to guide you in implementing these enhancements effectively.

The primary objective of this article is to demonstrate how to extend the functionality of an already working and well-tested piece of Python code. This is achieved by utilizing OpenAI's main feature: the ability to identify patterns and extrapolate by analogy.

The secondary objective here is to exhibit how to do it in no time and with maximum clarity and precision.

Prerequisites

In the prior article I've explained how to locate the code of application and its test suite in a single Babel code-snippet. It allows to read them smoothly altogever, and, nevertheless to launch the test suits and get results as well.

In the previous article, I explained how to locate both the application code and its test suite within a single Babel code snippet. This approach allows for seamless reading of both components while also enabling the execution of the test suite and obtaining results.

Concocting The Query

It may sound trivial, but all you need to do is clearly articulate what you really want in relation to what you currently have.

Explain What You Already Have

I've wrapped the app code and the test suite into a single Babel code snippet:

import unittest
from typing import Union
from fastapi import FastAPI
from fastapi.testclient import TestClient


app = FastAPI()


@app.get("/")
def read_root():
    return {"Hello": "World"}


@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
    return {"item_id": item_id, "q": q}
#
#
# The Test Suite
#
client = TestClient(app)

class TestFastAPI(unittest.TestCase):

    def test_read_root(self):
        response = client.get("/")
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.json(), {"Hello": "World"})

    def test_read_item_with_query(self):
        response = client.get("/items/42?q=somequery")
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.json(), {"item_id": 42, "q": "somequery"})

    def test_read_item_without_query(self):
        response = client.get("/items/42")
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.json(), {"item_id": 42, "q": None})

unittest.main(exit=False)
...
----------------------------------------------------------------------
Ran 3 tests in 0.026s

OK

and it works.

Explain What Do You Want

Now I want to:

extend the basic FastAPI application to use all HTTP request methods (GET, PUT, POST, DELETE, PATCH, HEAD, OPTIONS, TRACE); extend the test suite in accordion with these methods.

Retrieving The Answer

Certainly! Let's extend the FastAPI application to support all the HTTP request methods (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE), and also extend the test suite to test these endpoints.

Extended FastAPI Application with All HTTP Methods

import unittest
from typing import Union
from fastapi import FastAPI
from fastapi.testclient import TestClient

app = FastAPI()

# In-memory storage for demonstration purposes
items = {}

@app.get("/")
def read_root():
    return {"Hello": "World"}

@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
    return {"item_id": item_id, "q": q}

@app.post("/items/")
def create_item(item_id: int, item: dict):
    items[item_id] = item
    return {"item_id": item_id, "item": item}

@app.put("/items/{item_id}")
def update_item(item_id: int, item: dict):
    if item_id not in items:
        return {"Error": "Item not found"}, 404
    items[item_id] = item
    return {"item_id": item_id, "item": item}

@app.delete("/items/{item_id}")
def delete_item(item_id: int):
    if item_id not in items:
        return {"Error": "Item not found"}, 404
    del items[item_id]
    return {"Message": "Item deleted"}

@app.patch("/items/{item_id}")
def patch_item(item_id: int, item: dict):
    if item_id not in items:
        return {"Error": "Item not found"}, 404
    items[item_id].update(item)
    return {"item_id": item_id, "item": items[item_id]}

@app.head("/items/{item_id}")
def head_item(item_id: int):
    if item_id not in items:
        return {"Error": "Item not found"}, 404
    return items[item_id]

@app.options("/items/{item_id}")
def options_item(item_id: int):
    return {"Allow": ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"]}

@app.trace("/items/")
def trace_items():
    return {"Message": "TRACE endpoint"}
#
#
# The Test Suite
#
client = TestClient(app)

class TestFastAPI(unittest.TestCase):

    def test_read_root(self):
        response = client.get("/")
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.json(), {"Hello": "World"})

    def test_read_item_with_query(self):
        response = client.get("/items/42?q=somequery")
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.json(), {"item_id": 42, "q": "somequery"})

    def test_read_item_without_query(self):
        response = client.get("/items/42")
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.json(), {"item_id": 42, "q": None})

    def test_create_item(self):
        response = client.post("/items/", json={"item_id": 1, "item": {"name": "Item1"}})
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.json(), {"item_id": 1, "item": {"name": "Item1"}})

    def test_update_item(self):
        client.put("/items/", json={"item_id": 1, "item": {"name": "Item1"}})
        response = client.put("/items/1", json={"name": "Updated Item1"})
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.json(), {"item_id": 1, "item": {"name": "Updated Item1"}})

    def test_delete_item(self):
        client.post("/items/", json={"item_id": 1, "item": {"name": "Item1"}})
        response = client.delete("/items/1")
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.json(), {"Message": "Item deleted"})

    def test_patch_item(self):
        client.post("/items/", json={"item_id": 1, "item": {"name": "Item1"}})
        response = client.patch("/items/1", json={"description": "Updated description"})
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.json(), {"item_id": 1, "item": {"name": "Item1", "description": "Updated description"}})

    def test_head_item(self):
        client.post("/items/", json={"item_id": 1, "item": {"name": "Item1"}})
        response = client.head("/items/1")
        self.assertEqual(response.status_code, 200)

    def test_options_item(self):
        response = client.options("/items/1")
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.json(), {"Allow": ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"]})

    def test_trace_items(self):
        response = client.request("TRACE", "/items/")
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.json(), {"Message": "TRACE endpoint"})


unittest.main(exit=False)
FF..F....F
======================================================================
FAIL: test_create_item (__main__.TestFastAPI.test_create_item)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/babel-C8lSuP/python-7NQNHi", line 84, in test_create_item
    self.assertEqual(response.status_code, 200)
AssertionError: 422 != 200

======================================================================
FAIL: test_delete_item (__main__.TestFastAPI.test_delete_item)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/babel-C8lSuP/python-7NQNHi", line 97, in test_delete_item
    self.assertDictEqual(response.json(), {"Message": "Item deleted"})
AssertionError: [{'Error': 'Item not found'}, 404] is not an instance of <class 'dict'> : First argument is not a dictionary

======================================================================
FAIL: test_patch_item (__main__.TestFastAPI.test_patch_item)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/babel-C8lSuP/python-7NQNHi", line 103, in test_patch_item
    self.assertDictEqual(response.json(), {"item_id": 1, "item": {"name": "Item1", "description": "Updated description"}})
AssertionError: [{'Error': 'Item not found'}, 404] is not an instance of <class 'dict'> : First argument is not a dictionary

======================================================================
FAIL: test_update_item (__main__.TestFastAPI.test_update_item)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/babel-C8lSuP/python-7NQNHi", line 91, in test_update_item
    self.assertDictEqual(response.json(), {"item_id": 1, "item": {"name": "Updated Item1"}})
AssertionError: [{'Error': 'Item not found'}, 404] is not an instance of <class 'dict'> : First argument is not a dictionary

----------------------------------------------------------------------
Ran 10 tests in 0.037s

FAILED (failures=4)

Explanation of the Extended Application

  • GET /items/{item_id}: Retrieves an item with optional query parameters.
  • POST /items/: Creates a new item.
  • PUT /items/{item_id}: Updates an existing item.
  • DELETE /items/{item_id}: Deletes an item.
  • PATCH /items/{item_id}: Partially updates an item.
  • HEAD /items/{item_id}: Retrieves metadata for an item.
  • OPTIONS /items/{item_id}: Lists allowed methods.
  • TRACE /items/: Echoes the received request, used for diagnostics.

Explanation of the Extended Test Suite

  • Test for Each HTTP Method: Each test validates the respective HTTP method:
    • test_read_root: Tests the root GET / endpoint.
    • test_read_item_with_query and test_read_item_without_query: Test the GET /items/{item_id} endpoint with and without query parameters.
    • test_create_item: Tests the POST /items/ endpoint.
    • test_update_item: Tests the PUT /items/{item_id} endpoint.
    • test_delete_item: Tests the DELETE /items/{item_id} endpoint.
    • test_patch_item: Tests the PATCH /items/{item_id} endpoint.
    • test_head_item: Tests the HEAD /items/{item_id} endpoint.
    • test_options_item: Tests the OPTIONS /items/{item_id} endpoint.
    • test_trace_items: Tests the TRACE /items/ endpoint.

Conclusion

This setup ensures your FastAPI application correctly handles all major HTTP request methods and that the corresponding endpoints function as expected. You can simply run this code block in your Org-mode file, and it will execute the tests for the extended FastAPI application.