package caddyhugo
import (
"encoding/json"
"io/ioutil"
"os"
"os/exec"
"path"
"sync"
"testing"
"time"
"git.stephensearles.com/stephen/acedoc"
)
type World struct {
CH * CaddyHugo
BlogFolder string
}
func ( w * World ) Clean ( ) {
if w . BlogFolder != "" {
os . RemoveAll ( w . BlogFolder )
}
}
func NewWorld ( t * testing . T ) * World {
dir , err := ioutil . TempDir ( "" , "caddy-hugo2-test-" )
if err != nil {
t . Fatalf ( "error initializing test environment: %v" , err )
}
w := & World { BlogFolder : dir }
cmd := exec . Command ( "hugo" , "new" , "site" , dir )
cmd . Dir = dir
out , err := cmd . CombinedOutput ( )
if err != nil {
t . Fatalf ( "error initializing test site: %v\n\n%v" , err , string ( out ) )
}
w . CH = & CaddyHugo { }
w . CH . Setup ( dir )
return w
}
func TestEdits ( t * testing . T ) {
w := NewWorld ( t )
defer w . Clean ( )
const title = "sometitle"
var contentPath = path . Join ( "content" , title + ".md" )
w . CH . NewContent ( title , "" )
send := [ ] acedoc . Delta {
acedoc . Insert ( 0 , 0 , "hello" ) ,
acedoc . Insert ( 0 , 5 , " world" ) ,
acedoc . Insert ( 0 , 11 , " world" ) ,
}
var mtx sync . Mutex
received := [ ] acedoc . Delta { }
doc , err := w . CH . editSession ( contentPath )
if err != nil {
t . Fatal ( "error creating document client:" , err )
}
doc . doc . Client ( acedoc . DeltaHandlerFunc ( func ( ds [ ] acedoc . Delta ) error {
// receive some deltas...
mtx . Lock ( )
defer mtx . Unlock ( )
received = append ( received , ds ... )
return nil
} ) )
_ , ok := w . CH . hasEditSession ( contentPath )
if ! ok {
t . Fatal ( "expected there to be an established client" )
}
doc . doc . Apply ( send ... )
<- time . After ( 5 * time . Second )
mtx . Lock ( )
defer mtx . Unlock ( )
if len ( received ) != len ( send ) {
t . Errorf ( "expected %d deltas, received %d; expected: %v, received: %v" , len ( send ) , len ( received ) , send , received )
}
}
type WebsocketTester struct {
receivedPointer int
received [ ] [ ] byte
wroteMessages [ ] Message
wroteDeltas [ ] acedoc . Delta
mtx sync . Mutex
}
func ( ws * WebsocketTester ) ReadJSON ( v interface { } ) error {
ws . mtx . Lock ( )
defer ws . mtx . Unlock ( )
if len ( ws . received ) <= ws . receivedPointer {
return nil
}
err := json . Unmarshal ( ws . received [ ws . receivedPointer ] , v )
ws . receivedPointer ++
return err
}
func ( ws * WebsocketTester ) WriteJSON ( v interface { } ) error {
ws . mtx . Lock ( )
defer ws . mtx . Unlock ( )
m , ok := v . ( Message )
if ! ok {
panic ( "wrong type written to WebsocketTester" )
}
if len ( m . Deltas ) == 0 {
return nil
}
ws . wroteMessages = append ( ws . wroteMessages , m )
ws . wroteDeltas = append ( ws . wroteDeltas , m . Deltas ... )
return nil
}
func ( ws * WebsocketTester ) ReceiveJSON ( v interface { } ) error {
ws . mtx . Lock ( )
defer ws . mtx . Unlock ( )
out , err := json . Marshal ( v )
if err != nil {
return err
}
ws . received = append ( ws . received , out )
return nil
}
func TestDeltasSingle ( t * testing . T ) {
w := NewWorld ( t )
defer w . Clean ( )
const title = "test"
_ , err := w . CH . NewContent ( title , "" )
if err != nil {
t . Fatal ( "couldn't create new content:" , err )
}
client := new ( WebsocketTester )
doc , err := w . CH . editSession ( "content/" + title + ".md" )
if err != nil {
t . Fatal ( "couldn't establish docref for client 0:" , err )
}
go w . CH . handleDeltaConn ( client , doc )
a := acedoc . Insert ( 0 , 0 , "a" )
// pretend to get one sent from the "browser"
client . ReceiveJSON ( w . CH . Message ( a ) )
// wait to make sure it was processed
time . Sleep ( 50 * time . Millisecond )
// we shouldn't have written back to the client,
// so we expect to have written 0 messages
if len ( client . wroteMessages ) != 0 {
t . Errorf ( "client wrote %d messages, should have written %d" , len ( client . wroteMessages ) , 0 )
}
// we received one, so make sure that's counted properly
if len ( client . received ) != 1 {
t . Errorf ( "client has %d messages, should have received %d" , len ( client . received ) , 1 )
}
}
func TestDeltasDouble ( t * testing . T ) {
w := NewWorld ( t )
defer w . Clean ( )
const title = "test"
_ , err := w . CH . NewContent ( title , "" )
if err != nil {
t . Fatal ( "couldn't create new content:" , err )
}
clientA := new ( WebsocketTester )
clientB := new ( WebsocketTester )
doc , err := w . CH . editSession ( "content/" + title + ".md" )
if err != nil {
t . Fatal ( "couldn't establish docref for client 0:" , err )
}
go w . CH . handleDeltaConn ( clientA , doc )
go w . CH . handleDeltaConn ( clientB , doc )
// send the first message, simulating the browser on clientA
clientA . ReceiveJSON ( w . CH . Message ( acedoc . Insert ( 0 , 0 , "a" ) ) )
time . Sleep ( 100 * time . Millisecond )
clientA . mtx . Lock ( )
clientB . mtx . Lock ( )
// so we expect clientA to have written 0 messages, and
// clientB to have written 1
if len ( clientA . wroteMessages ) != 0 || len ( clientB . wroteMessages ) != 1 {
t . Errorf ( "clientA wrote %d messages, should have written 0. clientB wrote %d, should have written 1" , len ( clientA . wroteMessages ) , len ( clientB . wroteMessages ) )
}
// we received one via clientA and zero via clientB, so make sure
// that's counted properly
if len ( clientA . received ) != 1 || len ( clientB . received ) != 0 {
t . Errorf ( "clientA has %d messages, should have received 1; clientB has %d messages, should have received 0" , len ( clientA . received ) , len ( clientB . received ) )
}
clientA . mtx . Unlock ( )
clientB . mtx . Unlock ( )
// send the second message, via clientB
clientB . ReceiveJSON ( w . CH . Message ( acedoc . Insert ( 0 , 0 , "b" ) ) )
time . Sleep ( 400 * time . Millisecond )
clientA . mtx . Lock ( )
clientB . mtx . Lock ( )
// so we expect clientA to have written 1 message this time, and
// clientB to have written nothing new, so 1 still
if len ( clientA . wroteMessages ) != 1 || len ( clientB . wroteMessages ) != 1 {
t . Errorf ( "clientA wrote %d messages, should have written 1. clientB wrote %d, should have written 1 (just from before)" , len ( clientA . wroteMessages ) , len ( clientB . wroteMessages ) )
}
// we received zero (new) via clientA and one via clientB, so make sure
// that's counted properly
if len ( clientA . received ) != 1 || len ( clientB . received ) != 1 {
t . Errorf ( "clientA has %d messages, should have received 1; clientB has %d messages, should have received 1" , len ( clientA . received ) , len ( clientB . received ) )
}
clientA . mtx . Unlock ( )
clientB . mtx . Unlock ( )
}
func TestDeltasMulti ( t * testing . T ) {
w := NewWorld ( t )
defer w . Clean ( )
const title = "test"
_ , err := w . CH . NewContent ( title , "" )
if err != nil {
t . Fatal ( "couldn't create new content:" , err )
}
clients := [ ] * WebsocketTester { { } , { } , { } }
doc , err := w . CH . editSession ( "content/" + title + ".md" )
if err != nil {
t . Fatal ( "couldn't establish docref:" , err )
}
go w . CH . handleDeltaConn ( clients [ 0 ] , doc )
go w . CH . handleDeltaConn ( clients [ 1 ] , doc )
go w . CH . handleDeltaConn ( clients [ 2 ] , doc )
a := acedoc . Insert ( 0 , 0 , "a" )
b := acedoc . Insert ( 0 , 0 , "b" )
c := acedoc . Insert ( 0 , 0 , "c" )
clients [ 0 ] . ReceiveJSON ( w . CH . Message ( a ) )
clients [ 1 ] . ReceiveJSON ( w . CH . Message ( b ) )
clients [ 2 ] . ReceiveJSON ( w . CH . Message ( c ) )
time . Sleep ( 400 * time . Millisecond )
for i , client := range clients {
client . mtx . Lock ( )
// all clients should have "written" 2 deltas (could be the same
// message) that came from the other clients
if len ( client . wroteDeltas ) != 2 {
t . Errorf ( "client %d wrote %d deltas, should have written 2" , i , len ( client . wroteDeltas ) )
}
// all clients "received" 1 message from the "browser"
if len ( client . received ) != 1 {
t . Errorf ( "client %d has %d messages, should have received 1" , i , len ( client . received ) )
}
client . mtx . Unlock ( )
}
}
func TestPagesInPagesOut ( t * testing . T ) {
w := NewWorld ( t )
defer w . Clean ( )
// check there's no content at first
c , err := GetContent ( w . BlogFolder , w . CH . HugoSites )
if err != nil {
t . Fatalf ( "couldn't get content from a blank test environment: %v" , err )
}
if len ( c ) != 0 {
t . Fatalf ( "expected a blank test environment, but saw %d pages" , len ( c ) )
}
titles := [ ] string {
"test1" ,
"TEST 2!!" ,
}
found := map [ string ] bool { }
// create some known content
for i , title := range titles {
w . CH . NewContent ( title , "" )
c , err = GetContent ( w . BlogFolder , w . CH . HugoSites )
if err != nil {
t . Fatalf ( "couldn't get content from the test environment: %v" , err )
}
if len ( c ) - 1 != i {
t . Fatalf ( "expected %d page, but saw %d pages" , i + 1 , len ( c ) )
}
}
// make sure we get the content out that we just created
c , err = GetContent ( w . BlogFolder , w . CH . HugoSites )
if err != nil {
t . Fatalf ( "couldn't get content from the test environment: %v" , err )
}
for _ , content := range c {
if content . Metadata == nil {
t . Errorf ( "didn't see metadata for %q" , content . Filename )
}
found [ content . Filename ] = true
}
var missingSomething bool
for _ , title := range titles {
adjusted := path . Join ( "content" , docname ( title ) + ".md" )
if ! found [ adjusted ] {
missingSomething = true
t . Errorf ( "expected to find title %q, but didn't see it" , adjusted )
}
}
if missingSomething {
t . Logf ( "found titles: %v" , found )
}
}