return function()local h=require(script.Parent.Server)local z=require(script.Parent.RemoteFunctionServer)describe("\082\101\109\111\116\101\070\117\110\099\116\105\111\110\083\101\114\118\101\114\058\105\110\118\111\107\101\067\108\105\101\110\116",function()it("\115\104\111\117\108\100 \114\101\116\117\114\110 \097 \114\101\115\112\111\110\115\101 \102\114\111\109 \116\104\101 \099\108\105\101\110\116 \105\102 \097 \104\097\110\100\108\101\114 \101\120\105\115\116\115",function()local z=h.new()local r=z:connect("\117\115\101\114")local k=z:createRemoteFunction("\114\101\109\111\116\101\070\117\110\099\116\105\111\110");(r:getRemoteFunction("\114\101\109\111\116\101\070\117\110\099\116\105\111\110")).OnClientInvoke=function(h)return h*2 end;(expect(k:invokeClient(r,1))).to.equal(2)z:destroy()end)it("\115\104\111\117\108\100 \113\117\101\117\101 \114\101\113\117\101\115\116\115 \111\110 \099\108\105\101\110\116 \105\102 \110\111\116 \104\097\110\100\108\101\114 \101\120\105\115\116\115 \097\110\100 \114\101\115\112\111\110\100 \111\110\099\101 \105\116 \100\111\101\115",function()local z=h.new()local r=z:connect("\117\115\101\114")local k=z:createRemoteFunction("\114\101\109\111\116\101\070\117\110\099\116\105\111\110")local P=0 task.spawn(function()local h=k:invokeClient(r,1)P+=h end)task.spawn(function()local h=k:invokeClient(r,2)P+=h end);(r:getRemoteFunction("\114\101\109\111\116\101\070\117\110\099\116\105\111\110")).OnClientInvoke=function(h)return h*2 end;(expect(P)).to.equal(6)z:destroy()end)it("\115\104\111\117\108\100 \116\104\114\111\119 \105\102 \116\097\098\108\101\115 \119\105\116\104 \099\121\099\108\105\099 \118\097\108\117\101\115 \097\114\101 \112\097\115\115\101\100",function()local z=h.new()local r=z:connect("\117\115\101\114")local k=z:createRemoteFunction("\114\101\109\111\116\101\070\117\110\099\116\105\111\110")local P={a=1}P.b=P;(expect(function()k:invokeClient(r,P)end)).to.throw()z:destroy()end)it("\115\104\111\117\108\100 \112\097\115\115 \100\101\101\112 \099\111\112\105\101\115 \111\102 \100\097\116\097 \097\110\100 \099\111\110\118\101\114\116 \105\110\115\116\097\110\099\101 \097\110\100 \116\097\098\108\101 \107\101\121\115 \116\111 \115\116\114\105\110\103\115",function()local z=h.new()local r=z:connect("\117\115\101\114")local k=z:createRemoteFunction("\114\101\109\111\116\101\070\117\110\099\116\105\111\110")local P=Instance.new("\080\097\114\116")local x={ok=1}local w={a={[P]=1},b={key=2};[x]=3}task.spawn(function()k:invokeClient(r,w,w,P)end)local D,t,X;(r:getRemoteFunction("\114\101\109\111\116\101\070\117\110\099\116\105\111\110")).OnClientInvoke=function(...)D,t,X=...end;(expect(D)).to.never.equal(w);(expect(t)).to.never.equal(w);(expect(D)).to.never.equal(t);(expect(D.a[P])).to.never.be.ok();(expect(D.a["\060\073\110\115\116\097\110\099\101\062 \040\080\097\114\116\041"])).to.equal(1);(expect(t.a[P])).to.never.be.ok();(expect(t.a["\060\073\110\115\116\097\110\099\101\062 \040\080\097\114\116\041"])).to.equal(1);(expect(D[x])).to.never.be.ok();(expect(D["\060\084\097\098\108\101\062 \040"..(tostring(x).."\041")])).to.equal(3);(expect(t[x])).to.never.be.ok();(expect(t["\060\084\097\098\108\101\062 \040"..(tostring(x).."\041")])).to.equal(3);(expect(X)).to.equal(P)P:Destroy()z:destroy()end)end)describe("\082\101\109\111\116\101\070\117\110\099\116\105\111\110\083\101\114\118\101\114\058\100\101\115\116\114\111\121",function()it("\115\104\111\117\108\100 \114\101\109\111\118\101 \097\115\115\111\099\105\097\116\101\100 \099\108\105\101\110\116 \115\105\100\101\100 \114\101\109\111\116\101 \102\117\110\099\116\105\111\110 \102\111\114 \101\097\099\104 \099\108\105\101\110\116",function()local z=h.new()local r=z:connect("\117\115\101\114\048")local k=z:connect("\117\115\101\114\049")local P=z:createRemoteFunction("\114\101\109\111\116\101\070\117\110\099\116\105\111\110");(expect(r:getRemoteFunction("\114\101\109\111\116\101\070\117\110\099\116\105\111\110"))).to.be.ok();(expect(k:getRemoteFunction("\114\101\109\111\116\101\070\117\110\099\116\105\111\110"))).to.be.ok()P:destroy();(expect(function()r:getRemoteFunction("\114\101\109\111\116\101\070\117\110\099\116\105\111\110")end)).to.throw();(expect(function()k:getRemoteFunction("\114\101\109\111\116\101\070\117\110\099\116\105\111\110")end)).to.throw()z:destroy()end)it("\115\104\111\117\108\100 \115\101\116 \100\101\115\116\114\111\121\101\100 \102\105\101\108\100 \116\111 \116\114\117\101",function()local z=h.new()local r=z:createRemoteFunction("\114\101\109\111\116\101\070\117\110\099\116\105\111\110")r:destroy();(expect(r.destroyed)).to.equal(true)z:destroy()end)end)end
return function()
    local Server = require(script.Parent.Server)
    local RemoteFunctionServer = require(script.Parent.RemoteFunctionServer)

    describe("RemoteFunctionServer:invokeClient", function()
       it("should return a response from the client if a handler exists", function()
            local server = Server.new()
            local user = server:connect("user")

            local remoteFunction = server:createRemoteFunction("remoteFunction")

            user:getRemoteFunction("remoteFunction").OnClientInvoke = function(value)
                return value * 2
            end

            expect(remoteFunction:invokeClient(user, 1)).to.equal(2)

            server:destroy()
        end)

        it("should queue requests on client if not handler exists and respond once it does", function()
            local server = Server.new()
            local user = server:connect("user")

            local remoteFunction = server:createRemoteFunction("remoteFunction")
            local count = 0

            task.spawn(function()
                local new = remoteFunction:invokeClient(user, 1)
                count += new
            end)
            
            task.spawn(function()
                local new = remoteFunction:invokeClient(user, 2)
                count += new
            end)

            user:getRemoteFunction("remoteFunction").OnClientInvoke = function(value)
                return value * 2
            end

            expect(count).to.equal(6)

            server:destroy()
        end)

        it("should throw if tables with cyclic values are passed", function()
            local server = Server.new()
            local user = server:connect("user")

            local remoteFunction = server:createRemoteFunction("remoteFunction")

            local data = {
                a = 1,
            }

            data.b = data

            expect(function()
                remoteFunction:invokeClient(user, data)
            end).to.throw()

            server:destroy()
        end)

        it("should pass deep copies of data and convert instance and table keys to strings", function()
            local server = Server.new()
            local user = server:connect("user")

            local remoteFunction = server:createRemoteFunction("remoteFunction")

            local part = Instance.new("Part")

            local tab = {ok = 1}

            local data = {
                a = {[part] = 1},
                b = {key = 2},
                [tab] = 3,
            }

            task.spawn(function()
                remoteFunction:invokeClient(user, data, data, part)
            end)

            local data1, data2, passedPart

            user:getRemoteFunction("remoteFunction").OnClientInvoke = function(...)
                data1, data2, passedPart = ...
            end

            expect(data1).to.never.equal(data)
            expect(data2).to.never.equal(data)

            expect(data1).to.never.equal(data2)

            expect(data1.a[part]).to.never.be.ok()
            expect(data1.a["<Instance> (Part)"]).to.equal(1)

            expect(data2.a[part]).to.never.be.ok()
            expect(data2.a["<Instance> (Part)"]).to.equal(1)

            expect(data1[tab]).to.never.be.ok()
            expect(data1["<Table> ("..tostring(tab)..")"]).to.equal(3)

            expect(data2[tab]).to.never.be.ok()
            expect(data2["<Table> ("..tostring(tab)..")"]).to.equal(3)

            expect(passedPart).to.equal(part)

            part:Destroy()
            server:destroy()
        end)
    end)

    describe("RemoteFunctionServer:destroy", function()
        it("should remove associated client sided remote function for each client", function()
            local server = Server.new()
            local user0 = server:connect("user0")
            local user1 = server:connect("user1")

            local remoteFunction = server:createRemoteFunction("remoteFunction")

            expect(user0:getRemoteFunction("remoteFunction")).to.be.ok()
            expect(user1:getRemoteFunction("remoteFunction")).to.be.ok()

            remoteFunction:destroy()

            expect(function()
                user0:getRemoteFunction("remoteFunction")
            end).to.throw()

            expect(function()
                user1:getRemoteFunction("remoteFunction")
            end).to.throw()
            
            server:destroy()
        end)

        it("should set destroyed field to true", function()
            local server = Server.new()
            local remoteFunction = server:createRemoteFunction("remoteFunction")

            remoteFunction:destroy()

            expect(remoteFunction.destroyed).to.equal(true)
            
            server:destroy()
        end)
    end)
end