import supertest from "supertest";

import CsrfProtectionComponent from "../src/components/CsrfProtectionComponent.js";
import Controller from "../src/Controller.js";
import TestApp from "../src/TestApp.js";
import useApp from "./_app.js";

let app: TestApp;
useApp(async (addr, port) => {
    return app = new class extends TestApp {
        protected async init(): Promise<void> {
            this.use(new class extends Controller {
                public routes(): void {
                    this.get('/', (req, res) => {
                        res.send(this.getApp().as(CsrfProtectionComponent).getSessionCsrfToken(req.getSession()));
                    }, 'csrf_test');

                    this.post('/', (req, res) => {
                        res.json({
                            status: 'ok',
                        });
                    }, 'csrf_test');
                }
            }());

            await super.init();
        }
    }('test', addr, port, true, false);
});

describe('Test CSRF protection', () => {
    let cookies: string[];
    let csrf: string;

    test('no csrf token should be in session at first', (done) => {
        const agent = supertest(app.getExpressApp());
        agent.post('/')
            .accept('json')
            .expect(401)
            .then(res => {
                expect(res.text).toContain(`You weren't assigned any CSRF token.`);
                cookies = res.get('Set-Cookie');

                agent.get('/')
                    .set('Cookie', cookies)
                    .expect(200)
                    .then(res => {
                        csrf = res.text;
                        done();
                    }).catch(done.fail);
            }).catch(done.fail);
    });

    test('sending no csrf token should fail', (done) => {
        expect(cookies).toBeDefined();

        const agent = supertest(app.getExpressApp());
        agent.post('/')
            .accept('json')
            .set('Cookie', cookies)
            .expect(401)
            .then((res) => {
                expect(res.text).toContain(`You didn't provide any CSRF token.`);
                done();
            }).catch(done.fail);
    });

    test('sending an invalid csrf token should fail', (done) => {
        expect(cookies).toBeDefined();

        const agent = supertest(app.getExpressApp());
        agent.post('/')
            .accept('json')
            .set('Cookie', cookies)
            .set('Content-Type', 'application/json')
            .send({csrf: 'not_a_valid_csrf'})
            .expect(401)
            .then(res => {
                expect(res.text).toContain(`Tokens don't match.`);
                done();
            }).catch(done.fail);
    });

    test('sending a valid csrf token should success', (done) => {
        expect(cookies).toBeDefined();

        const agent = supertest(app.getExpressApp());
        agent.post('/')
            .set('Cookie', cookies)
            .set('Content-Type', 'application/json')
            .send({csrf: csrf})
            .expect(200)
            .then(() => done())
            .catch(done.fail);
    });
});