bencode_spec.fnl

Branches > main > latest > files / nvim / pack / plugins / opt / conjure / fnl / conjure-spec / remote / transport / bencode_spec.fnl

(local {: describe : it} (require :plenary.busted))
(local assert (require :luassert.assert))
(local ffi (require :ffi))
(local bencode (require :conjure.remote.transport.bencode))

(fn buffer-content [bs]
  "Get the current buffer content as a string"
  (let [(ptr blen) (bs.buf:ref)]
    (ffi.string ptr blen)))

(fn assert-buffer [bs expected]
  "Assert buffer contains expected content"
  (assert.are.equals expected (buffer-content bs)))

(fn assert-stack-depth [bs expected]
  "Assert stack has expected depth"
  (assert.are.equals expected (length bs.stack)))

(fn assert-stack-empty [bs]
  "Assert stack is empty"
  (assert-stack-depth bs 0))

(describe "conjure.remote.transport.bencode"
  (fn []
    (describe "basic functionality"
      (fn []
        (let [bs (bencode.new)
              data {:foo [:bar]}]
          (it "buffer starts empty"
            (fn []
              (assert-buffer bs "")
              (assert-stack-empty bs)))
          (it "a single bencoded value"
            (fn []
              (assert.same [data] (bencode.decode-all bs (bencode.encode data)))))
          (it "buffer is empty after a decode"
            (fn []
              (assert-buffer bs "")
              (assert-stack-empty bs))))))

    (describe "dict key ordering"
      (fn []
        (it "encodes dict keys in sorted order"
          (fn []
            (assert.are.equals
              "d11:client-name7:conjure2:id3:abc2:op5:clonee"
              (bencode.encode {:op "clone" :id "abc" :client-name "conjure"}))))
        (it "sorts nested dicts and many keys"
          (fn []
            (assert.are.equals
              "d1:ai1e1:bi2e1:cd1:xi0e1:yi0e1:zi0ee1:di4e1:ei5e1:fi6e1:gi7e1:hi8ee"
              (bencode.encode {:h 8 :g 7 :f 6 :e 5 :d 4
                               :c {:z 0 :y 0 :x 0}
                               :b 2 :a 1}))))))

    (describe "multiple-values"
      (fn []
        (let [bs (bencode.new)
              data-a {:foo [:bar]}
              data-b [1 2 3]]
          (it "buffer starts empty"
            (fn []
              (assert-buffer bs "")
              (assert-stack-empty bs)))
          (it "two bencoded values"
            (fn []
              (assert.same [data-a data-b]
                (bencode.decode-all bs
                  (.. (bencode.encode data-a)
                      (bencode.encode data-b))))))
          (it "buffer is empty after a decode"
            (fn []
              (assert-buffer bs "")
              (assert-stack-empty bs))))))

    (describe "partial list parsing"
      (fn []
        (let [bs (bencode.new)
              data-a {:foo [:bar]}
              data-b [1 2 3]
              encoded-b (bencode.encode data-b)]
          (it "buffer starts empty"
            (fn []
              (assert-buffer bs "")
              (assert-stack-empty bs)))
          (it "first value with partial list start"
            (fn []
              (assert.same [data-a]
                (bencode.decode-all bs
                  (.. (bencode.encode data-a)
                      (string.sub encoded-b 1 3))))))
          (it "after first, buffer contains partial integer and list is on stack"
            (fn []
              (assert-buffer bs "i1")
              (assert-stack-depth bs 1)
              (assert.are.equals "list" (. bs.stack 1 :t))))
          (it "second value completes the list"
            (fn []
              (assert.same [data-b]
                (bencode.decode-all bs (string.sub encoded-b 4)))))
          (it "buffer is empty after completion"
            (fn []
              (assert-buffer bs "")
              (assert-stack-empty bs))))))

    (describe "integer edge cases"
      (fn []
        (it "incomplete integer at start"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [] (bencode.decode-all bs "i"))
              (assert-buffer bs "i")
              (assert-stack-empty bs))))
        (it "incomplete integer with partial number"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [] (bencode.decode-all bs "i123"))
              (assert-buffer bs "i123")
              (assert-stack-empty bs))))
        (it "complete integer after partial"
          (fn []
            (let [bs (bencode.new)]
              (bencode.decode-all bs "i123")
              (assert.same [123] (bencode.decode-all bs "e"))
              (assert-buffer bs "")
              (assert-stack-empty bs))))
        (it "negative integer split"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [] (bencode.decode-all bs "i-"))
              (assert-buffer bs "i-")
              (assert.same [-42] (bencode.decode-all bs "42e"))
              (assert-buffer bs "")
              (assert-stack-empty bs))))))

    (describe "string edge cases"
      (fn []
        (it "incomplete string length"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [] (bencode.decode-all bs "3"))
              (assert-buffer bs "3")
              (assert-stack-empty bs))))
        (it "incomplete string with colon but no content"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [] (bencode.decode-all bs "3:"))
              (assert-buffer bs "3:")
              (assert-stack-empty bs))))
        (it "incomplete string with partial content"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [] (bencode.decode-all bs "3:fo"))
              (assert-buffer bs "3:fo")
              (assert-stack-empty bs))))
        (it "complete string after partial"
          (fn []
            (let [bs (bencode.new)]
              (bencode.decode-all bs "3:fo")
              (assert.same ["foo"] (bencode.decode-all bs "o"))
              (assert-buffer bs "")
              (assert-stack-empty bs))))
        (it "zero-length string"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [""] (bencode.decode-all bs "0:"))
              (assert-buffer bs "")
              (assert-stack-empty bs))))
        (it "multi-digit length split across chunks"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [] (bencode.decode-all bs "1"))
              (assert.same [] (bencode.decode-all bs "0"))
              (assert.same [] (bencode.decode-all bs ":"))
              (assert.same [] (bencode.decode-all bs "hello"))
              (assert.same ["helloworld"] (bencode.decode-all bs "world"))
              (assert-buffer bs "")
              (assert-stack-empty bs))))))

    (describe "list edge cases"
      (fn []
        (it "empty list"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [[]] (bencode.decode-all bs "le"))
              (assert-buffer bs "")
              (assert-stack-empty bs))))
        (it "list start only"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [] (bencode.decode-all bs "l"))
              (assert-buffer bs "")
              (assert-stack-depth bs 1)
              (assert.are.equals "list" (. bs.stack 1 :t)))))
        (it "nested list start"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [] (bencode.decode-all bs "ll"))
              (assert-buffer bs "")
              (assert-stack-depth bs 2)
              (assert.are.equals "list" (. bs.stack 1 :t))
              (assert.are.equals "list" (. bs.stack 2 :t)))))
        (it "complete nested empty lists"
          (fn []
            (let [bs (bencode.new)]
              (bencode.decode-all bs "ll")
              (assert.same [[[]]] (bencode.decode-all bs "ee"))
              (assert-buffer bs "")
              (assert-stack-empty bs))))
        (it "list with mixed incomplete elements"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [] (bencode.decode-all bs "li42"))
              (assert-buffer bs "i42")
              (assert-stack-depth bs 1)
              (bencode.decode-all bs "e3:fo")
              (assert-buffer bs "3:fo")
              (assert.same [[42 "foo"]] (bencode.decode-all bs "oe"))
              (assert-buffer bs "")
              (assert-stack-empty bs))))))

    (describe "dict edge cases"
      (fn []
        (it "empty dict"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [{}] (bencode.decode-all bs "de"))
              (assert-buffer bs "")
              (assert-stack-empty bs))))
        (it "dict start only"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [] (bencode.decode-all bs "d"))
              (assert-buffer bs "")
              (assert-stack-depth bs 1)
              (assert.are.equals "dict" (. bs.stack 1 :t)))))
        (it "dict with incomplete key"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [] (bencode.decode-all bs "d3:fo"))
              (assert-buffer bs "3:fo")
              (assert-stack-depth bs 1))))
        (it "dict with complete key but no value"
          (fn []
            (let [bs (bencode.new)]
              (bencode.decode-all bs "d3:fo")
              (assert.same [] (bencode.decode-all bs "o"))
              (assert-buffer bs "")
              (assert-stack-depth bs 1)
              (assert.are.equals "foo" (. bs.stack 1 :k)))))
        (it "dict with incomplete value"
          (fn []
            (let [bs (bencode.new)]
              (bencode.decode-all bs "d3:foo")
              (assert.same [] (bencode.decode-all bs "i4"))
              (assert-buffer bs "i4")
              (assert-stack-depth bs 1)
              (assert.are.equals "foo" (. bs.stack 1 :k)))))
        (it "complete dict after partial parsing"
          (fn []
            (let [bs (bencode.new)]
              (bencode.decode-all bs "d3:fooi4")
              (assert.same [{:foo 42}] (bencode.decode-all bs "2ee"))
              (assert-buffer bs "")
              (assert-stack-empty bs))))
        (it "nested dict in list"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [] (bencode.decode-all bs "ld"))
              (assert-stack-depth bs 2)
              (assert.are.equals "list" (. bs.stack 1 :t))
              (assert.are.equals "dict" (. bs.stack 2 :t)))))
        (it "list in dict value"
          (fn []
            (let [bs (bencode.new)]
              (bencode.decode-all bs "d3:fool")
              (assert-stack-depth bs 2)
              (assert.are.equals "dict" (. bs.stack 1 :t))
              (assert.are.equals "list" (. bs.stack 2 :t))
              (assert.are.equals "foo" (. bs.stack 1 :k)))))))

    (describe "boundary conditions"
      (fn []
        (it "single character chunks"
          (fn []
            (let [bs (bencode.new)
                  encoded (bencode.encode {:key "value"})]
              (var final-result [])
              (for [i 1 (length encoded)]
                (let [result (bencode.decode-all bs (string.sub encoded i i))]
                  (each [_ v (ipairs result)]
                    (table.insert final-result v))))
              (assert.same [{:key "value"}] final-result)
              (assert-buffer bs "")
              (assert-stack-empty bs))))
        (it "exact boundary splits"
          (fn []
            (let [bs (bencode.new)]
              (assert.same [123] (bencode.decode-all bs "i123e3:"))
              (assert.same ["foo"] (bencode.decode-all bs "foo"))
              (assert.same [] (bencode.decode-all bs ""))
              (assert-buffer bs "")
              (assert-stack-empty bs))))
        (it "large nested structure split"
          (fn []
            (let [bs (bencode.new)
                  data {:numbers [1 2 3] :nested {:inner "value"}}
                  encoded (bencode.encode data)
                  mid (math.floor (/ (length encoded) 2))]
              (assert.same [] (bencode.decode-all bs (string.sub encoded 1 mid)))
              (assert.same [data] (bencode.decode-all bs (string.sub encoded (+ mid 1))))
              (assert-buffer bs "")
              (assert-stack-empty bs))))
        (it "very large data structure in 32-char chunks"
          (fn []
            (let [bs (bencode.new)
                  item {:id "item" :data "some-long-data-string-that-takes-space" :nums [1 2 3 4 5]}
                  list []]
              (for [_ 1 50]
                (table.insert list item))
              (let [encoded (bencode.encode list)
                    chunk-size 32
                    final-result []]
                (for [start 1 (length encoded) chunk-size]
                  (let [end (math.min (+ start chunk-size -1) (length encoded))
                        chunk (string.sub encoded start end)
                        result (bencode.decode-all bs chunk)]
                    (each [_ v (ipairs result)]
                      (table.insert final-result v))))
                (assert.same [list] final-result)
                (assert-buffer bs "")
                (assert-stack-empty bs)))))))))