#!/bin/bash

# Tests for Serval rhizome operations.
#
# Copyright 2012 The Serval Project, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

source "${0%/*}/../testframework.sh"
source "${0%/*}/../testdefs.sh"
source "${0%/*}/../testdefs_rhizome.sh"

shopt -s extglob

setup_rhizome() {
   set_instance +A
   executeOk_servald config set debug.rhizome on
   create_rhizome_identities 1
   set_instance +B
   executeOk_servald config set debug.rhizome on
   create_rhizome_identities 4
   assert [ $SIDB1 != $SIDA1 ]
   assert [ $SIDB2 != $SIDA1 ]
   assert [ $SIDB3 != $SIDA1 ]
   assert [ $SIDB4 != $SIDA1 ]
}

doc_InitialEmptyList="Initial list is empty"
setup_InitialEmptyList() {
   setup_servald
   setup_rhizome
}
test_InitialEmptyList() {
   executeOk_servald rhizome list ''
   assert_rhizome_list
}

doc_AddNoAuthorNoManifest="Add with no author and no manifest file"
setup_AddNoAuthorNoManifest() {
   setup_servald
   setup_rhizome
   executeOk_servald rhizome list ''
   assert_rhizome_list
   echo "A test file" >file1
   echo "Another test file" >file2
}
test_AddNoAuthorNoManifest() {
   executeOk_servald rhizome add file '' '' file1
   assert_stdout_add_file file1
}

doc_AddNoManifest="Add with no manifest file"
setup_AddNoManifest() {
   setup_servald
   setup_rhizome
   executeOk_servald rhizome list ''
   assert_rhizome_list
   echo "A test file" >file1
}
test_AddNoManifest() {
   executeOk_servald rhizome add file $SIDB1 '' file1
   assert_stdout_add_file file1
}

doc_AddNoAuthor="Add with no author makes manifest without BK"
setup_AddNoAuthor() {
   setup_servald
   setup_rhizome
   echo "A test file" >file1
}
test_AddNoAuthor() {
   executeOk_servald rhizome add file '' '' file1 file1.manifest
   assert_stdout_add_file file1
   assertGrep --matches=0 file1.manifest '^BK='
}

doc_AddNonExistManifest="Add with non-existent manifest file"
setup_AddNonExistManifest() {
   setup_servald
   setup_rhizome
   executeOk_servald rhizome list ''
   assert_rhizome_list
   echo "A test file" >file1
   echo "Another test file" >file2
}
test_AddNonExistManifest() {
   assert --error-on-fail [ ! -e file1.manifest ]
   executeOk_servald rhizome add file $SIDB1 '' file1 file1.manifest
   assert_stdout_add_file file1
   assert [ -r file1.manifest ]
   assert_manifest_complete file1.manifest
   assertGrep file1.manifest '^service=file$'
   assertGrep file1.manifest '^name=file1$'
}

doc_AddManifest="Add with minimal manifest file"
setup_AddManifest() {
   setup_servald
   setup_rhizome
   executeOk_servald rhizome list ''
   assert_rhizome_list
   echo "A test file" >file1
   echo -e 'name=wah\ndate=12345' >file1.manifest
   echo "Another test file" >file2
}
test_AddManifest() {
   executeOk_servald rhizome add file $SIDB1 '' file1 file1.manifest
   tfw_cat --stdout --stderr -v file1.manifest
   assert_stdout_add_file file1 name=wah
   assert_manifest_complete file1.manifest
   assertGrep file1.manifest '^service=file$'
   assertGrep file1.manifest '^name=wah$'
   assertGrep file1.manifest '^date=12345$'
}

doc_AddEmpty="Add with empty payload"
setup_AddEmpty() {
   setup_servald
   setup_rhizome
   executeOk_servald rhizome list ''
   assert_rhizome_list
}
test_AddEmpty() {
   executeOk_servald rhizome add file $SIDB1 '' '' .manifest
   tfw_cat --stdout --stderr -v .manifest
   assert_stdout_add_file ''
   assert_manifest_complete .manifest
   assertGrep .manifest '^service=file$'
   assertGrep .manifest '^name=$'
   assertGrep .manifest '^filesize=0$'
}

doc_AddThenList="List contains one file after one add"
setup_AddThenList() {
   setup_servald
   setup_rhizome
   executeOk_servald rhizome list ''
   assert_rhizome_list
   echo "A test file" >file1
   echo "Another test file" >file2
}
test_AddThenList() {
   # Add first file
   executeOk_servald rhizome add file $SIDB1 '' file1 file1.manifest
   executeOk_servald rhizome list ''
   assert_rhizome_list file1
   # Add second file
   executeOk_servald rhizome add file $SIDB1 '' file2 file2.manifest
   executeOk_servald rhizome list ''
   assert_rhizome_list file1 file2
}

doc_AddThenExtractManifest="Extract manifest after one add"
setup_AddThenExtractManifest() {
   setup_servald
   setup_rhizome
   echo "A test file" >file1
   executeOk_servald rhizome add file $SIDB1 '' file1 file1.manifest
   executeOk_servald rhizome list ''
   assert_rhizome_list file1
   extract_manifest_id manifestid file1.manifest
   extract_manifest_version version file1.manifest
   extract_manifest_filehash filehash file1.manifest
}
test_AddThenExtractManifest() {
   executeOk_servald rhizome extract manifest $manifestid file1x.manifest
   assert cmp file1.manifest file1x.manifest
   assertStdoutLineCount '==' 6
   local size=$(( $(cat file1 | wc -c) + 0 ))
   assertStdoutGrep --matches=1 "^service:file$"
   assertStdoutGrep --matches=1 "^manifestid:$manifestid$"
   assertStdoutGrep --matches=1 "^version:$version$"
   assertStdoutGrep --matches=1 "^inserttime:[0-9]\+$"
   assertStdoutGrep --matches=1 "^filehash:$filehash$"
   assertStdoutGrep --matches=1 "^filesize:$size$"
}

doc_ExtractMissingManifest="Extract non-existent manifest"
setup_ExtractMissingManifest() {
   setup_servald
   setup_rhizome
   manifestid=0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
}
test_ExtractMissingManifest() {
   execute --exit-status=1 $servald rhizome extract manifest $manifestid foo.manifest
   assertStdoutLineCount '==' 0
   assert [ ! -e foo.manifest ]
}

doc_ExtractManifestInvalidID="Extract manifest using invalid ID"
setup_ExtractManifestInvalidID() {
   setup_servald
   setup_rhizome
}
test_ExtractManifestInvalidID() {
   execute --exit-status=255 $servald rhizome extract manifest 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEx foo.manifest
   assertStdoutLineCount '==' 0
   assert [ ! -e foo.manifest ]
   execute --exit-status=255 $servald rhizome extract manifest 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE foo.manifest
   assertStdoutLineCount '==' 0
   assert [ ! -e foo.manifest ]
   execute --exit-status=255 $servald rhizome extract manifest '' foo.manifest
   assertStdoutLineCount '==' 0
   assert [ ! -e foo.manifest ]
}

doc_AddThenExtractFile="Extract file after one add"
setup_AddThenExtractFile() {
   setup_servald
   setup_rhizome
   echo "A test file" >file1
   executeOk_servald rhizome add file $SIDB1 '' file1 file1.manifest
   tfw_cat --stderr
   executeOk_servald rhizome list ''
   assert_rhizome_list file1
   extract_manifest_filehash filehash file1.manifest
}
test_AddThenExtractFile() {
   executeOk_servald rhizome extract file $filehash file1x
   assert cmp file1 file1x
   local size=$(( $(cat file1 | wc -c) + 0 ))
   assertStdoutLineCount '==' 2
   assertStdoutGrep --matches=1 "^filehash:$filehash$"
   assertStdoutGrep --matches=1 "^filesize:$size$"
}

doc_ExtractMissingFile="Extract non-existent file"
setup_ExtractMissingFile() {
   setup_servald
   setup_rhizome
   filehash=0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
}
test_ExtractMissingFile() {
   execute --exit-status=1 $servald rhizome extract file $filehash foo
   assertStdoutLineCount '==' 0
   assert [ ! -e foo ]
}

doc_ExtractFileInvalidID="Extract file using invalid ID"
setup_ExtractFileInvalidID() {
   setup_servald
   setup_rhizome
}
test_ExtractFileInvalidID() {
   execute --exit-status=255 $servald rhizome extract file 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEx foo
   assertStdoutLineCount '==' 0
   assert [ ! -e foo ]
   execute --exit-status=255 $servald rhizome extract file 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE foo
   assertStdoutLineCount '==' 0
   assert [ ! -e foo ]
   execute --exit-status=255 $servald rhizome extract file '' foo
   assertStdoutLineCount '==' 0
   assert [ ! -e foo ]
}

doc_AddDuplicate="Add same manifest detects duplicate"
setup_AddDuplicate() {
   setup_servald
   setup_rhizome
   executeOk_servald rhizome list ''
   assert_rhizome_list
   echo "A test file" >file1
   echo "Another test file" >file2
   echo "A test file, second version" >file1_2
   # Add first file
   executeOk_servald rhizome add file $SIDB1 '' file1 file1.manifest
   extract_stdout_secret file1_secret
   # Add second file
   executeOk_servald rhizome add file $SIDB1 '' file2 file2.manifest
   extract_stdout_secret file2_secret
   # Make sure they are both in the list.
   executeOk_servald rhizome list ''
   assert_rhizome_list file1 file2
}
test_AddDuplicate() {
   # Add first file again - nothing should change in its manifests, and it
   # should appear that the add command succeeded (with perhaps some grumbling
   # on stderr).
   execute --exit-status=2 $servald rhizome add file $SIDB1 '' file1 file1.manifestA
   assert [ -s file1.manifestA ]
   assert_stdout_add_file file1
   executeOk_servald rhizome list ''
   assert_rhizome_list file1 file2
   strip_signatures file1.manifest file1.manifestA
   assert diff file1.manifest file1.manifestA
   # Repeat for second file.
   execute --exit-status=2 $servald rhizome add file $SIDB1 '' file2 file2.manifestA
   assert [ -s file2.manifestA ]
   assert_stdout_add_file file2
   executeOk_servald rhizome list ''
   assert_rhizome_list file1 file2
   strip_signatures file2.manifest file2.manifestA
   assert diff file2.manifest file2.manifestA
}

doc_AddMismatched="Add mismatched manifest/payload fails"
setup_AddMismatched() {
   setup_AddDuplicate
}
test_AddMismatched() {
   # Try to add another file using an existing manifest, should fail and leave
   # the manifest file unchanged.
   cp file1.manifest file1_2.manifest
   execute $servald rhizome add file $SIDB1 '' file1_2 file1_2.manifest
   assertExitStatus '!=' 0
   assert cmp file1.manifest file1_2.manifest
   # And rhizome store should be unchanged.
   executeOk_servald rhizome list ''
   assert_rhizome_list file1 file2
}

doc_AddUpdateSameVersion="Add new payload to existing manifest with same version fails"
setup_AddUpdateSameVersion() {
   setup_AddDuplicate
   cp file1.manifest file1_2.manifest
   strip_signatures file1_2.manifest
   sed -i -e '/^date=/d;/^filehash=/d;/^filesize=/d' file1_2.manifest
   tfw_cat -v file1_2.manifest
   assertGrep --matches=0 file1_2.manifest '^filehash='
   extract_manifest_version '' file1_2.manifest # asserts has version= line
   assertGrep file1_2.manifest '^id='
   cp file1_2.manifest file1_2.manifest.orig
}
test_AddUpdateSameVersion() {
   tfw_cat -v file1_2.manifest
   execute $servald rhizome add file $SIDB1 '' file1_2 file1_2.manifest
   assertExitStatus --stderr '!=' 0
   tfw_cat -v file1_2.manifest
   assert cmp file1_2.manifest file1_2.manifest.orig
   # And rhizome store should be unchanged.
   executeOk_servald rhizome list ''
   assert_rhizome_list file1 file2
}

doc_AddUpdateNewVersion="Add new payload to existing manifest with new version"
setup_AddUpdateNewVersion() {
   setup_AddUpdateSameVersion
   extract_manifest_version version file1_2.manifest
   let version=version+1
   sed -i -e "/^version=/s/=.*/=$version/" file1_2.manifest
   assertGrep --matches=1 file1_2.manifest "^version=$version$"
}
test_AddUpdateNewVersion() {
   tfw_cat -v file1_2.manifest
   executeOk_servald rhizome add file $SIDB1 '' file1_2 file1_2.manifest
   assert_stdout_add_file file1_2 name=file1
   assert_manifest_newer file1.manifest file1_2.manifest
   # Rhizome store contents reflect new payload.
   executeOk_servald rhizome list ''
   assert_rhizome_list file1_2 file2
}

doc_AddUpdateNoAuthor="Cannot add new payload to authorless manifest"
setup_AddUpdateNoAuthor() {
   setup_AddUpdateNewVersion
   sed -i -e '/^BK=/d' file1_2.manifest
}
test_AddUpdateNoAuthor() {
   tfw_cat -v file1_2.manifest
   execute $servald rhizome add file $SIDB1 '' file1_2 file1_2.manifest
   tfw_cat --stderr
   assertExitStatus '!=' 0
   # Rhizome store contents have old payload.
   executeOk_servald rhizome list ''
   assert_rhizome_list file1 file2
}

doc_AddUpdateNoAuthorWithSecret="Add new payload to authorless manifest with bundle secret"
setup_AddUpdateNoAuthorWithSecret() {
   setup_AddUpdateNoAuthor
}
test_AddUpdateNoAuthorWithSecret() {
   tfw_cat -v file1_2.manifest
   executeOk_servald rhizome add file $SIDB1 '' file1_2 file1_2.manifest "$file1_secret"
   tfw_cat --stderr
   # Rhizome store contents have new payload.
   executeOk_servald rhizome list ''
   assert_rhizome_list file1_2! file2
}

doc_AddUpdateAutoVersion="Add new payload to existing manifest with automatic version"
setup_AddUpdateAutoVersion() {
   setup_AddUpdateSameVersion
   sed -i -e '/^version=/d' file1_2.manifest
   assertGrep --matches=0 file1_2.manifest '^version='
}
test_AddUpdateAutoVersion() {
   tfw_cat -v file1_2.manifest
   sleep 0.001 # Ensure that at least one millisecond has elapsed
   executeOk_servald rhizome add file $SIDB1 '' file1_2 file1_2.manifest
   assert_manifest_newer file1.manifest file1_2.manifest
   # Rhizome store contents reflect new payload.
   executeOk_servald rhizome list ''
   assert_rhizome_list file1_2 file2
}

doc_AddUnsupportedService="Add with unsupported service fails"
setup_AddUnsupportedService() {
   setup_servald
   setup_rhizome
   echo "Message1" >file1
   echo -e 'service=Fubar' >file1.manifest
}
test_AddUnsupportedService() {
   execute $servald rhizome add file $SIDB1 '' file1 file1.manifest
   assertExitStatus '!=' 0
}

doc_MeshMSAddCreate="First add MeshMS creates manifest"
setup_MeshMSAddCreate() {
   setup_servald
   setup_rhizome
   echo "Message1" >file1
   echo -e "service=MeshMS1\nsender=$SIDB1\nrecipient=$SIDB2" >file1.manifest
}
test_MeshMSAddCreate() {
   executeOk_servald rhizome add file $SIDB1 '' file1 file1.manifest
   assert_stdout_add_file file1
   assert_manifest_complete file1.manifest
   executeOk_servald rhizome list ''
   assert_rhizome_list file1
   extract_manifest_filehash filehash file1.manifest
   executeOk_servald rhizome extract file $filehash file1x
   assert diff file1 file1x
}

doc_MeshMSAddGrow="Subsequent add MeshMS updates manifest and removes old payload"
setup_MeshMSAddGrow() {
   setup_servald
   setup_rhizome
   echo "Message1" >file1
   echo -e "service=MeshMS1\nsender=$SIDB1\nrecipient=$SIDB2" >file1.manifest
}
test_MeshMSAddGrow() {
   executeOk_servald rhizome add file $SIDB1 '' file1 file1.manifest
   assert_stdout_add_file file1
   assert_manifest_complete file1.manifest
   executeOk_servald rhizome list ''
   assert_rhizome_list file1
   extract_manifest_id id file1.manifest
   extract_manifest_filehash filehash file1.manifest
   extract_manifest_BK bk file1.manifest
   local -a ofilehashes=()
   for m in 2 3 4 5; do
      ofilehashes+=("$filehash")
      echo -e "id=$id\nBK=$bk\nservice=MeshMS1\nsender=$SIDB1\nrecipient=$SIDB2" >file1.manifest
      echo "Message$m" >>file1
      executeOk_servald rhizome add file $SIDB1 '' file1 file1.manifest
      executeOk_servald rhizome list ''
      assert_rhizome_list file1
      extract_manifest_id idx file1.manifest
      extract_manifest_filehash filehashx file1.manifest
      extract_manifest_BK bkx file1.manifest
      compute_filehash filehash file1
      assert --message="manifest ID remains the same" [ "$idx" = "$id" ]
      assert --message="manifest BK remains the same" [ "$bkx" = "$bk" ]
      assert --message="filehash is for new file" [ "$filehash" = "$filehashx" ]
      executeOk_servald rhizome extract file "$filehash" file1x
      assert --message="extracted payload is correct" diff file1 file1x
      for ofilehash in "${ofilehashes[@]}"; do
         execute --exit-status=1 --stderr $servald rhizome extract file "$ofilehash"
      done
   done
}

doc_MeshMSAddMissingSender="Add MeshMS without sender fails"
setup_MeshMSAddMissingSender() {
   setup_servald
   setup_rhizome
   echo "Message1" >file1
   echo -e "service=MeshMS1\nrecipient=$SIDB2" >file1.manifest
}
test_MeshMSAddMissingSender() {
   execute $servald rhizome add file $SIDB1 '' file1 file1.manifest
   assertExitStatus '!=' 0
}

doc_MeshMSAddMissingRecipient="Add MeshMS without recipient fails"
setup_MeshMSAddMissingRecipient() {
   setup_servald
   setup_rhizome
   executeOk_servald rhizome list ''
   assert_rhizome_list
   echo "Message1" >file1
   echo -e "service=MeshMS1\nsender=$SIDB1" >file1.manifest
}
test_MeshMSAddMissingRecipient() {
   execute $servald rhizome add file $SIDB1 '' file1 file1.manifest
   assertExitStatus '!=' 0
}

doc_MeshMSAddMissingAuthor="Add MeshMS without author uses sender"
setup_MeshMSAddMissingAuthor() {
   setup_servald
   setup_rhizome
   echo "Message1" >file1
   echo -e "service=MeshMS1\nsender=$SIDB1\nrecipient=$SIDB2" >file1.manifest
}
test_MeshMSAddMissingAuthor() {
   executeOk_servald rhizome add file '' '' file1 file1.manifest
   assert_stdout_add_file file1
   assert_manifest_complete file1.manifest
   executeOk_servald rhizome list ''
   assert_rhizome_list file1
}

doc_MeshMSListFilter="List MeshMS manifests by filter"
setup_MeshMSListFilter() {
   setup_servald
   setup_rhizome
   echo "Message1" >file1
   echo -e "service=MeshMS1\nsender=$SIDB1\nrecipient=$SIDB2" >file1.manifest
   echo "Message2" >file2
   echo -e "service=MeshMS1\nsender=$SIDB1\nrecipient=$SIDB3" >file2.manifest
   echo "Message3" >file3
   echo -e "service=MeshMS1\nsender=$SIDB1\nrecipient=$SIDB4" >file3.manifest
   echo "Message3" >file4
   echo -e "service=MeshMS1\nsender=$SIDB2\nrecipient=$SIDB3" >file4.manifest
   executeOk_servald rhizome add file '' '' file1 file1.manifest
   assert_stdout_add_file file1
   assert_manifest_complete file1.manifest
   executeOk_servald rhizome add file '' '' file2 file2.manifest
   assert_stdout_add_file file2
   assert_manifest_complete file2.manifest
   executeOk_servald rhizome add file '' '' file3 file3.manifest
   assert_stdout_add_file file3
   assert_manifest_complete file3.manifest
   executeOk_servald rhizome add file '' '' file4 file4.manifest
   assert_stdout_add_file file4
   assert_manifest_complete file4.manifest
   executeOk_servald rhizome list ''
   assert_rhizome_list file1 file2 file3 file4
}
test_MeshMSListFilter() {
   executeOk_servald rhizome list '' file
   assert_rhizome_list
   executeOk_servald rhizome list '' MeshMS1
   assert_rhizome_list file1 file2 file3 file4
   executeOk_servald rhizome list '' '' $SIDB1
   assert_rhizome_list file1 file2 file3
   executeOk_servald rhizome list '' '' $SIDB2
   assert_rhizome_list file4
   executeOk_servald rhizome list '' '' $SIDB3
   assert_rhizome_list
   executeOk_servald rhizome list '' '' $SIDB4
   assert_rhizome_list
   executeOk_servald rhizome list '' '' '' $SIDB1
   assert_rhizome_list
   executeOk_servald rhizome list '' '' '' $SIDB2
   assert_rhizome_list file1
   executeOk_servald rhizome list '' '' '' $SIDB3
   assert_rhizome_list file2 file4
   executeOk_servald rhizome list '' file '' $SIDB3
   assert_rhizome_list
   executeOk_servald rhizome list '' '' '' $SIDB4
   assert_rhizome_list file3
   executeOk_servald rhizome list '' '' $SIDB1 $SIDB4
   assert_rhizome_list file3
   executeOk_servald rhizome list '' '' $SIDB2 $SIDB4
   assert_rhizome_list
   executeOk_servald rhizome list '' '' $SIDB2 $SIDB3
   assert_rhizome_list file4
}

doc_ImportForeignBundle="Can import a bundle created by another instance"
setup_ImportForeignBundle() {
   setup_servald
   setup_rhizome
   set_instance +A
   echo "Hello from A" >fileA
   executeOk_servald rhizome add file $SIDA1 '' fileA fileA.manifest
   assert_stdout_add_file fileA
   set_instance +B
}
test_ImportForeignBundle() {
   executeOk_servald rhizome import bundle fileA fileA.manifest
   assert_stdout_import_bundle fileA
   executeOk_servald rhizome list ''
   assert_rhizome_list fileA!
}

runTests "$@"