#include "../test.h"

#define TEST(name) TEST_CASE("send-frame: " name, "[send-frame]")

TEST("server -> client frame") {
    EstablishedServerParser p;

    SECTION("small frame") {
        string payload = "preved"; // must be <= 125
        auto bin = p.start_message().send(payload, IsFinal::YES);
        CHECK_BINFRAME(bin).final().opcode(Opcode::BINARY).payload(payload).binlen(2 + 6); // 2 header + 6 payload
        std::vector<string> vpl = {"pr", "ev", "ed"};
        auto bin2 = p.start_message().send(vpl.begin(), vpl.end(), IsFinal::YES);
        CHECK((bin2 == bin)); // check iterator mode
    }

    SECTION("medium frame") {
        string payload = repeat("preved", 100); // must be: 125 < X < 65536
        auto bin = p.start_message().send(payload, IsFinal::YES);
        CHECK_BINFRAME(bin).final().opcode(Opcode::BINARY).payload(payload).binlen(2 + 2 + 600); // 2 header + 2 length + 600 payload
    }

    SECTION("big frame") {
        string payload = repeat("preved!", 10000); // must be > 65536
        auto bin = p.start_message().send(payload, IsFinal::YES);
        CHECK_BINFRAME(bin).final().opcode(Opcode::BINARY).payload(payload).binlen(2 + 8 + 70000);
    }
}

TEST("client -> server frame") {
    EstablishedClientParser p;

    SECTION("small frame") {
        string payload = "preved"; // must be <= 125
        auto bin = p.start_message(Opcode::TEXT).send(payload, IsFinal::YES);
        CHECK_BINFRAME(bin).mask(bin.substr(2, 4)).final().opcode(Opcode::TEXT).payload(payload).binlen(2 + 4 + 6); // 2 header + 4 mask + 6 payload

        EstablishedServerParser p;
        auto f = get_frame(p, bin);
        CHECK_FRAME(f).final().payload(payload);
    }

    SECTION("medium frame") {
        string payload = repeat("preved", 100); // must be: 125 < X < 65536
        auto bin = p.start_message(Opcode::TEXT).send(payload, IsFinal::YES);
        CHECK_BINFRAME(bin).mask(bin.substr(4, 4)).final().opcode(Opcode::TEXT).payload(payload).binlen(2 + 2 + 4 + 600); // 2 header + 2 length + 4 mask + 600 payload
    }

    SECTION("big frame") {
        string payload = repeat("preved!", 10000); // must be > 65536
        auto bin = p.start_message(Opcode::TEXT).send(payload, IsFinal::YES);
        CHECK_BINFRAME(bin).mask(bin.substr(10, 4)).final().opcode(Opcode::TEXT).payload(payload).binlen(2 + 8 + 4 + 70000);
    }
}

TEST("empty frame still masked") {
    EstablishedClientParser p;
    auto bin = p.start_message(Opcode::BINARY).send("", IsFinal::YES);
    CHECK_BINFRAME(bin).mask(bin.substr(2, 4)).final().opcode(Opcode::BINARY).binlen(2 + 4);
}

TEST("opcode CONTINUE is forced for fragment frames of message (including final frame)") {
    EstablishedServerParser p;
    auto m1 = p.start_message(Opcode::BINARY);
    auto bin = m1.send("frame1");
    CHECK_BINFRAME(bin).opcode(Opcode::BINARY).payload("frame1");
    bin = m1.send("frame2");
    CHECK_BINFRAME(bin).opcode(Opcode::CONTINUE).payload("frame2");
    bin = m1.send("frame3", IsFinal::YES);
    CHECK_BINFRAME(bin).final().opcode(Opcode::CONTINUE).payload("frame3");

    auto m2 = p.start_message(Opcode::TEXT);
    bin = m2.send("frame4");
    CHECK_BINFRAME(bin).opcode(Opcode::TEXT).payload("frame4");
    bin = m2.send("frame5", IsFinal::YES); // reset frame count
    CHECK_BINFRAME(bin).final().opcode(Opcode::CONTINUE).payload("frame5");
}

TEST("control frame send") {
    EstablishedServerParser p;
    auto bin = p.send_control(Opcode::PING, "myping");
    CHECK_BINFRAME(bin).final().opcode(Opcode::PING).payload("myping");
    bin = p.send_control(Opcode::PONG, "mypong");
    CHECK_BINFRAME(bin).final().opcode(Opcode::PONG).payload("mypong");
    bin = p.send_control(Opcode::CLOSE, "myclose");
    CHECK_BINFRAME(bin).final().opcode(Opcode::CLOSE).payload("myclose");
}

TEST("frame count survives control message in the middle") {
    EstablishedServerParser p;
    auto m = p.start_message();
    auto bin = m.send("frame1");
    CHECK_BINFRAME(bin).opcode(Opcode::BINARY).payload("frame1");
    bin = p.send_control(Opcode::PING, "");
    CHECK_BINFRAME(bin).final().opcode(Opcode::PING);
    bin = m.send("frame2");
    CHECK_BINFRAME(bin).opcode(Opcode::CONTINUE).payload("frame2");
    bin = p.send_control(Opcode::PONG, "");
    CHECK_BINFRAME(bin).final().opcode(Opcode::PONG);
    bin = m.send("frame3", IsFinal::YES);
    CHECK_BINFRAME(bin).final().opcode(Opcode::CONTINUE).payload("frame3");
}

TEST("attempt to send frame after sending final frame") {
    EstablishedServerParser p;
    auto m = p.start_message();
    auto bin = m.send("payload", IsFinal::YES);
    CHECK(!bin.empty());
    REQUIRE_THROWS_AS(m.send("beyond payload", IsFinal::YES), Error);
    REQUIRE_THROWS_AS(m.send("beyond payload", IsFinal::NO), Error);
}

TEST("attempt to start another message, having unfinished one") {
    EstablishedServerParser p;
    auto m = p.start_message();
    REQUIRE_THROWS_AS(p.start_message(), Error);
    m.send("hello ");
    REQUIRE_THROWS_AS(p.start_message(), Error);
    m.send("world", IsFinal::YES);
    REQUIRE_NOTHROW(p.start_message());
};