test.rkt
#lang racket
(require "main.rkt"
         "binary.rkt"
         tests/eli-tester)

(define-syntax-rule
  (with-memcached p e ...)
  (local [(define sp #f)]
    (dynamic-wind
     (λ () 
       (define-values (the-sp stdout stdin stderr) (subprocess (current-output-port) #f (current-error-port) "/opt/local/bin/memcached" "-p" (number->string p)))
       (set! sp the-sp)
       (sleep 1))
     (λ () e ...)
     (λ () (subprocess-kill sp #t)))))

(define-syntax with-memcacheds
  (syntax-rules ()
    [(_ () e ...) (let () e ...)]
    [(_ (p . ps) e ...)
     (with-memcached p (with-memcacheds ps e ...))]))

(test
 #:failure-prefix "Binary protocol"
 (test
  (with-output-to-bytes
      (lambda ()
        (write-get* 'Get #"Hello")))
  =>
  #"\x80\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Hello"
  (parameterize ([current-input-port
                  (open-input-bytes
                   #"\x81\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Not found")])
    (read-get*))
  =>
  (values #f
          #"\0\0\0\0\0\0\0\0")
  
  (parameterize ([current-input-port
                  (open-input-bytes
                   #"\x81\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xde\xad\xbe\xefWorld")])
    (read-get*))
  =>
  (values #"World"
          #"\0\0\0\0\0\0\0\1")
  
  (parameterize ([current-input-port
                  (open-input-bytes
                   #"\x81\x00\x00\x05\x04\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xde\xad\xbe\xefHelloWorld")])
    (read-get*))
  =>
  (values #"World"
          #"\0\0\0\0\0\0\0\1")
  (with-output-to-bytes
      (lambda ()
        (write-set* 'Add #"Hello" #"World" #"\xde\xad\xbe\xef" 3600 #"\0\0\0\0\0\0\0\0")))
  =>
  #"\x80\x02\x00\x05\x08\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xad\xbe\xef\x00\x00\x0e\x10HelloWorld"
  (parameterize ([current-input-port
                  (open-input-bytes
                   #"\x81\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")])
    (read-set*))
  =>
  #"\0\0\0\0\0\0\0\1"
  (with-output-to-bytes
      (lambda ()
        (write-delete* 'Delete #"Hello" #"\0\0\0\0\0\0\0\0")))
  =>
  #"\x80\x04\x00\x05\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Hello"
  (with-output-to-bytes
      (lambda ()
        (write-incr* 'Increment #"counter" 1 0 3600 #"\0\0\0\0\0\0\0\0")))
  =>
  #"\x80\x05\x00\x07\x14\x00\x00\x00\x00\x00\x00\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x10counter"
  (parameterize ([current-input-port
                  (open-input-bytes
                   #"\x81\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00")])
    (read-incr*))
  =>
  0
  (with-output-to-bytes
      (lambda ()
        (write-append* 'Append #"Hello" #"!" #"\0\0\0\0\0\0\0\0")))
  =>
  #"\x80\x0e\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Hello!")
 
 #:failure-prefix "Commands"
 (local
   [(define port 11211)
    (define mc #f)
    (define cas #f)
    (define-syntax-rule (value-1 e) (let-values ([(x y) e]) x))
    (define-syntax-rule (value-1n e) (integer-bytes->integer (value-1 e) #f #t))
    (define-syntax-rule (record-cas! e)
      (let-values ([(e-v e-cas) e])
        (set! cas e-cas)
        e-v))]
   (with-memcacheds ((+ port 0) (+ port 1) (+ port 2))
     (test
      (set! mc 
            (memcached
             "localhost" (+ port 0)
             "localhost" (+ port 1)
             "localhost" (+ port 2)))
      (memcached-set! mc #"foo" #"bar")
      (value-1 (memcached-get mc #"foo")) => #"bar"
      
      (memcached-add! mc #"foo" #"zog") => #f
      (value-1 (memcached-get mc #"foo")) => #"bar"
      
      (or (memcached-delete! mc #"zag") #t)
      (memcached-add! mc #"zag" #"zog")
      (value-1 (memcached-get mc #"zag")) => #"zog"
      
      (memcached-replace! mc #"zag" #"zig")
      (value-1 (memcached-get mc #"zag")) => #"zig"
      (memcached-replace! mc #"zig" #"zoo") => #f
      (value-1 (memcached-get mc #"zig")) => #f
      
      (memcached-set! mc #"list" #"2")
      (value-1 (memcached-get mc #"list")) => #"2"
      (memcached-append! mc #"list" #"3")
      (value-1 (memcached-get mc #"list")) => #"23"
      (memcached-prepend! mc #"list" #"1")
      (value-1 (memcached-get mc #"list")) => #"123"
      
      (record-cas! (memcached-get mc #"foo")) => #"bar"
      (memcached-set! mc #"foo" #:cas cas #"zog")
      (value-1 (memcached-get mc #"foo")) => #"zog"
      (memcached-set! mc #"foo" #"bleg")
      (value-1 (memcached-get mc #"foo")) => #"bleg"
      (memcached-set! mc #"foo" #:cas cas #"zig") => #f
      (value-1 (memcached-get mc #"foo")) => #"bleg"
      
      (memcached-delete! mc #"foo")
      (value-1 (memcached-get mc #"foo")) => #f
      
      (or (memcached-delete! mc #"k") #t)
      (memcached-incr! mc #"k") => #f
      (memcached-decr! mc #"k") => #f
      (memcached-set! mc #"k" #"0")
      (value-1 (memcached-get mc #"k")) => #"0"
      (memcached-incr! mc #"k")
      (value-1 (memcached-get mc #"k")) => #"1"
      (memcached-incr! mc #"k")
      (value-1 (memcached-get mc #"k")) => #"2"
      (memcached-decr! mc #"k")
      (value-1 (memcached-get mc #"k")) => #"1"
      (memcached-incr! mc #"k" #:amount 3)
      (value-1 (memcached-get mc #"k")) => #"4"
      (memcached-decr! mc #"k" #:amount 3)
      (value-1 (memcached-get mc #"k")) => #"1"
      
      ; XXX statistics
      ))))