#!/usr/bin/env qore
# -*- mode: qore; indent-tabs-mode: nil -*-

%require-types
%enable-all-warnings
%new-style
%strict-args
%allow-injection

%requires ../../../../qlib/Util.qm
%requires ../../../../qlib/QUnit.qm
%requires ../../../../qlib/Logger.qm
%requires ../../../../qlib/DataProvider
%requires ../../../../qlib/FsUtil.qm
%requires ../../../../qlib/FtpClientDataProvider

%include  ../../qore/classes/FtpServer.qc

%exec-class FtpClientDataProviderTest

public class FtpClientDataProviderTest inherits QUnit::Test {
    constructor() : Test("FtpClientDataProvider Test", "1.0") {
        addTestCase("test", \ftpDataProviderTest());

        # Return for compatibility with test harness that checks return value.
        set_return_value(main());
    }

    ftpDataProviderTest() {
        int port = getPort();

        # PORT only supports IPv4, so we can't use "localhost", which may resolve
        # to an IPv6 address
        FtpServer serv(port, m_options.verbose - 1, "127.0.0.1");
        on_exit serv.shutdown();

        serv.setFs();
        serv.setDirect();

        port = serv.getPort();
        string url = sprintf("ftp://user:pass@127.0.0.1:%d", port);

        FtpClientDataProvider root({"url": url});
        AbstractDataProvider prov = root.getChildProviderEx("create-file");
        hash<DataProviderSummaryInfo> sinfo = prov.getSummaryInfo();
        assertEq("create-file", sinfo.name);
        assertEq(Type::String, sinfo.desc.type());
        assertGt(0, sinfo.desc.size());

        string str = get_random_string();
        string path = "/test.txt";
        hash<auto> h = prov.doRequest({
            "path": path,
            "data": str,
        });
        assertEq(path, h.path);

        prov = root.getChildProviderEx("get-file");
        sinfo = prov.getSummaryInfo();
        assertEq("get-file", sinfo.name);
        assertEq(Type::String, sinfo.desc.type());
        assertGt(0, sinfo.desc.size());
        h = prov.doRequest({
            "path": path,
            "text": True,
        });
        assertEq(str, h."data");

        prov = root.getChildProviderEx("stat");
        sinfo = prov.getSummaryInfo();
        assertEq("stat", sinfo.name);
        assertEq(Type::String, sinfo.desc.type());
        h = prov.doRequest({
            "path": path,
        });
        assertEq({
            "name": "test.txt",
            "path": "/test.txt",
            "type": "REGULAR",
            "size": 15,
        }, h - "mtime");

        prov = root.getChildProviderEx("move");
        sinfo = prov.getSummaryInfo();
        assertEq("move", sinfo.name);
        assertEq(Type::String, sinfo.desc.type());
        /* test server doesn't support RNFR / RNTO yet
        string newpath = "/test1.txt";
        h = prov.doRequest({
            "source": path,
            "target": newpath,
        });
        assertEq(newpath, h.path);
        path = newpath;
        */

        prov = root.getChildProviderEx("delete");
        sinfo = prov.getSummaryInfo();
        assertEq("delete", sinfo.name);
        assertEq(Type::String, sinfo.desc.type());
        h = prov.doRequest({
            "path": path,
        });
        assertEq(path, h.path);

        prov = root.getChildProviderEx("stat");
        sinfo = prov.getSummaryInfo();
        assertEq("stat", sinfo.name);
        assertEq(Type::String, sinfo.desc.type());
        assertThrows("FTP-STAT-ERROR", \prov.doRequest(), {
            "path": path,
        });

        string dir = get_random_string();
        prov = root.getChildProviderEx("mkdir");
        sinfo = prov.getSummaryInfo();
        assertEq("mkdir", sinfo.name);
        assertEq(Type::String, sinfo.desc.type());
        h = prov.doRequest({
            "path": dir,
        });
        assertEq("/" + dir, h.path);

        prov = root.getChildProviderEx("stat");
        h = prov.doRequest({
            "path": dir,
        });
        assertEq("DIRECTORY", h.type);
    }

    private softint getPort() {
        return m_options.port ?? ENV.FTPCLIENT_TEST_PORT ?? 0;
    }
}
