My main suggestion for this code is for the `helper` function. It's not a good idea to only compare a few selected strings from the set of all interesting strings. It's far more efficient to just have a list of strings that are already ordered, and then ensure that this ordering is represented by the `<=>` operator under test.

To do this, each element should be compared to each element, including itself. The code I usually use to do this is:

~~~c++
#include <cassert>
#include <string>
#include <vector>
#include <iostream>

int sign(int i)
{
    return i < 0 ? -1 : i > 0 ? +1 : 0;
}

template<typename T>
int spaceship(T a, T b)
{
    return a < b ? -123 : a > b ? +123 : 0;
}

std::string op(int cmp)
{
    return cmp < 0 ? "<" : cmp > 0 ? ">" : "==";
}

template<typename T>
void test_spaceship(const std::vector<T> &elements)
{
    bool error = false;

    for (std::size_t i = 0; i < elements.size(); ++i) {
        for (std::size_t j = 0; j < elements.size(); ++j) {
            int expected = spaceship(i, j);
            int actual = spaceship(elements[i], elements[j]);
            if (sign(expected) != sign(actual)) {
                std::cerr << __func__ << ":\n";
                std::cerr << "  expected " << elements[i] << " "
                          << op(expected) << " " << elements[j] << "\n";
                std::cerr << "  but got  " << elements[i] << " "
                          << op(actual) << " " << elements[j] << "\n";
                error = true;
            }
        }
    }
    std::flush(std::cerr);
    assert(!error);
}

int main()
{
    std::vector<std::string> elements{
            "",
            "first",
            "second",
            "zfourth", // intentionally in the wrong order
            "third",
            "zzfifth"
    };

    test_spaceship(elements);
    std::cout << "ok\n";
    std::string s;
    std::getline(std::cin, s);
}
~~~

Of course you would have to adjust the code a bit to test your `Strong_String` instead of my `std::string`, but the general idea should get clear.

If you have some strings that are considered equal by your operator `<=>`, you would have to adjust the above code to have a `std::vector<std::vector<T>>`, but that should be equally easy.

----

My other favorite topic is the [cctype header](https://stackoverflow.com/questions/7131026/is-it-safe-to-call-the-functions-from-cctype-with-char-arguments) since you must never feed a plain character to functions like `isalnum` or `toupper`. Furthermore by doing this, you limit your program to 8-bit character sets, unless `CHAR_BIT` is greater than 8 on your machine, and chances are small for that.

You should rather treat your strings as Unicode strings, and that brings a whole new topic of decisions, such as sorting strings from different scripts and languages. But that's still better than being caught in the 1990s with their limited [code pages](https://en.wikipedia.org/wiki/Code_page).