mirror of
https://github.com/natelandau/shell-scripting-templates.git
synced 2025-11-08 13:13:47 -05:00
Unit tests
This commit is contained in:
@@ -129,6 +129,7 @@ Commonly used functions in many scripts
|
||||
* `_progressBar_` Prints a progress bar within a for/while loop
|
||||
* `_rootAvailable_` Validate we have superuser access as root (via sudo if requested)
|
||||
* `_runAsRoot_` Run the requested command as root (via sudo if requested)
|
||||
* `_safeExit_` Cleans up temporary files before exiting a script
|
||||
* `_seekConfirmation_` Seek user input for yes/no question
|
||||
* `_setPATH_` Add directories to $PATH so script can find executables
|
||||
|
||||
@@ -150,11 +151,9 @@ Common utilities for working with files.
|
||||
|
||||
* `_listFiles_` Find files in a directory. Use either glob or regex.
|
||||
* `_backupFile_` Creates a backup of a specified file with .bak extension or optionally to a specified directory.
|
||||
* `_cleanFilename_` Cleans a filename of all non-alphanumeric (or user specified) characters and overwrites original
|
||||
* `_parseFilename_` Break a filename into its component parts which and place them into prefixed variables (dir, basename, extension, full path, etc.)
|
||||
* `_parseFilename_` Break a filename into its component parts which and place them into prefixed variables for use in your script (dir, basename, extension, path, etc.)
|
||||
* `_decryptFile_` Decrypts a file with `openssl`
|
||||
* `_encryptFile_` Encrypts a file with `openssl`
|
||||
* `_ext_` Extract the extension from a filename
|
||||
* `_extract_` Extract a compressed file
|
||||
* `_json2yaml_` Convert JSON to YAML uses python
|
||||
* `_makeSymlink_` Creates a symlink and backs up a file which may be overwritten by the new symlink. If the exact same symlink already exists, nothing is done.
|
||||
|
||||
226
test/files.bats
226
test/files.bats
@@ -52,6 +52,8 @@ setup() {
|
||||
VERBOSE=false
|
||||
FORCE=false
|
||||
DRYRUN=false
|
||||
PASS=123
|
||||
|
||||
}
|
||||
|
||||
teardown() {
|
||||
@@ -62,8 +64,6 @@ teardown() {
|
||||
######## FIXTURES ########
|
||||
YAML1="${BATS_TEST_DIRNAME}/fixtures/yaml1.yaml"
|
||||
YAML1parse="${BATS_TEST_DIRNAME}/fixtures/yaml1.yaml.txt"
|
||||
YAML2="${BATS_TEST_DIRNAME}/fixtures/yaml2.yaml"
|
||||
JSON="${BATS_TEST_DIRNAME}/fixtures/json.json"
|
||||
unencrypted="${BATS_TEST_DIRNAME}/fixtures/test.md"
|
||||
encrypted="${BATS_TEST_DIRNAME}/fixtures/test.md.enc"
|
||||
|
||||
@@ -75,6 +75,24 @@ encrypted="${BATS_TEST_DIRNAME}/fixtures/test.md.enc"
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "_decryptFile_" {
|
||||
run _decryptFile_ "${encrypted}" "test-decrypted.md"
|
||||
assert_success
|
||||
assert_file_exist "test-decrypted.md"
|
||||
run cat "test-decrypted.md"
|
||||
assert_success
|
||||
assert_line --index 0 "# About"
|
||||
assert_line --index 1 "This repository contains everything needed to bootstrap and configure new Mac computer. Included here are:"
|
||||
}
|
||||
|
||||
@test "_encryptFile_" {
|
||||
run _encryptFile_ "${unencrypted}" "test-encrypted.md.enc"
|
||||
assert_success
|
||||
assert_file_exist "test-encrypted.md.enc"
|
||||
run cat "test-encrypted.md.enc"
|
||||
assert_line --index 0 --partial "Salted__"
|
||||
}
|
||||
|
||||
_testBackupFile_() {
|
||||
|
||||
@test "_backupFile_: no source" {
|
||||
@@ -152,19 +170,223 @@ _testListFiles_() {
|
||||
refute_output --partial "yestest1.txt"
|
||||
assert_output --partial "notest1.txt"
|
||||
}
|
||||
|
||||
@test "_listFiles: fail no args" {
|
||||
run _listFiles_
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "_listFiles: fail one arg" {
|
||||
run _listFiles_ "g"
|
||||
assert_failure
|
||||
}
|
||||
}
|
||||
|
||||
_testParseFilename_() {
|
||||
|
||||
@test "_parseFilename_: fail with no file" {
|
||||
run _parseFilename_ "somenonexistantfile"
|
||||
assert_failure
|
||||
assert_output --partial "Can't locate a file to parse"
|
||||
}
|
||||
|
||||
@test "_parseFilename_: file with one extension" {
|
||||
touch "testfile.txt"
|
||||
VERBOSE=true
|
||||
run _parseFilename_ "testfile.txt"
|
||||
|
||||
assert_success
|
||||
assert_line --index 0 --regexp "\[ debug\].*{PARSE_FULL}: /.*testfile\.txt$"
|
||||
assert_line --index 1 --regexp "\[ debug\].*${PARSE_BASE}: testfile\.txt$"
|
||||
assert_line --index 2 --regexp "\[ debug\].*${PARSE_PATH}: /.*"
|
||||
assert_line --index 3 --regexp "\[ debug\].*${PARSE_EXT}: txt$"
|
||||
assert_line --index 4 --regexp "\[ debug\].*${PARSE_BASENOEXT}: testfile$"
|
||||
}
|
||||
|
||||
@test "_parseFilename_: file with dots in name" {
|
||||
touch "testfile.for.testing.txt"
|
||||
VERBOSE=true
|
||||
run _parseFilename_ "testfile.for.testing.txt"
|
||||
|
||||
assert_success
|
||||
assert_line --index 0 --regexp "\[ debug\].*{PARSE_FULL}: /.*testfile\.for\.testing\.txt$"
|
||||
assert_line --index 1 --regexp "\[ debug\].*${PARSE_BASE}: testfile\.for\.testing\.txt$"
|
||||
assert_line --index 2 --regexp "\[ debug\].*${PARSE_PATH}: /.*"
|
||||
assert_line --index 3 --regexp "\[ debug\].*${PARSE_EXT}: txt$"
|
||||
assert_line --index 4 --regexp "\[ debug\].*${PARSE_BASENOEXT}: testfile\.for\.testing$"
|
||||
}
|
||||
|
||||
@test "_parseFilename_: file with no extension" {
|
||||
touch "testfile"
|
||||
VERBOSE=true
|
||||
run _parseFilename_ "testfile"
|
||||
|
||||
assert_success
|
||||
assert_line --index 0 --regexp "\[ debug\].*{PARSE_FULL}: /.*testfile$"
|
||||
assert_line --index 1 --regexp "\[ debug\].*${PARSE_BASE}: testfile$"
|
||||
assert_line --index 2 --regexp "\[ debug\].*${PARSE_PATH}: /.*"
|
||||
assert_line --index 3 --regexp "\[ debug\].*${PARSE_EXT}: $"
|
||||
assert_line --index 4 --regexp "\[ debug\].*${PARSE_BASENOEXT}: testfile$"
|
||||
}
|
||||
|
||||
@test "_parseFilename_: file with tar.gz" {
|
||||
touch "testfile.tar.gz"
|
||||
VERBOSE=true
|
||||
run _parseFilename_ "testfile.tar.gz"
|
||||
|
||||
assert_success
|
||||
assert_line --index 0 --regexp "\[ debug\].*{PARSE_FULL}: /.*testfile\.tar\.gz$"
|
||||
assert_line --index 1 --regexp "\[ debug\].*${PARSE_BASE}: testfile\.tar\.gz$"
|
||||
assert_line --index 2 --regexp "\[ debug\].*${PARSE_PATH}: /.*"
|
||||
assert_line --index 3 --regexp "\[ debug\].*${PARSE_EXT}: tar\.gz$"
|
||||
assert_line --index 4 --regexp "\[ debug\].*${PARSE_BASENOEXT}: testfile$"
|
||||
}
|
||||
|
||||
@test "_parseFilename_: file with three extensions" {
|
||||
touch "testfile.tar.gzip.bzip"
|
||||
VERBOSE=true
|
||||
run _parseFilename_ -n3 "testfile.tar.gzip.bzip"
|
||||
|
||||
assert_success
|
||||
assert_line --index 0 --regexp "\[ debug\].*{PARSE_FULL}: /.*testfile\.tar\.gzip\.bzip$"
|
||||
assert_line --index 1 --regexp "\[ debug\].*${PARSE_BASE}: testfile\.tar\.gzip\.bzip$"
|
||||
assert_line --index 2 --regexp "\[ debug\].*${PARSE_PATH}: /.*"
|
||||
assert_line --index 3 --regexp "\[ debug\].*${PARSE_EXT}: tar\.gzip\.bzip$"
|
||||
assert_line --index 4 --regexp "\[ debug\].*${PARSE_BASENOEXT}: testfile$"
|
||||
}
|
||||
|
||||
# _parseFilename_ "test.tar.gz"
|
||||
# _parseFilename_ "test.tar.gzip"
|
||||
}
|
||||
|
||||
_testMakeSymlink_() {
|
||||
|
||||
@test "_makeSymlink_: Fail with no source fire" {
|
||||
run _makeSymlink_ "sourceFile" "destFile"
|
||||
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "_makeSymlink_: fail with no specified destination" {
|
||||
touch "test.txt"
|
||||
run _makeSymlink_ "test.txt"
|
||||
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "_makeSymlink_: make link" {
|
||||
touch "test.txt"
|
||||
touch "test2.txt"
|
||||
run _makeSymlink_ "${TESTDIR}/test.txt" "${TESTDIR}/test2.txt"
|
||||
|
||||
assert_success
|
||||
assert_output --regexp "\[ info\] symlink /.*/test\.txt → /.*/test2\.txt"
|
||||
assert_link_exist "test2.txt"
|
||||
assert_file_exist "test2.txt.bak"
|
||||
}
|
||||
|
||||
@test "_makeSymlink_: Ignore already existing links" {
|
||||
touch "test.txt"
|
||||
ln -s "$(realpath test.txt)" "${TESTDIR}/test2.txt"
|
||||
run _makeSymlink_ "$(realpath test.txt)" "${TESTDIR}/test2.txt"
|
||||
|
||||
assert_success
|
||||
assert_link_exist "test2.txt"
|
||||
assert_output --regexp "\[ info\] Symlink already exists: /.*/test\.txt → /.*/test2\.txt"
|
||||
}
|
||||
|
||||
@test "_makeSymlink_: Don't make backup" {
|
||||
touch "test.txt"
|
||||
touch "test2.txt"
|
||||
run _makeSymlink_ -n "${TESTDIR}/test.txt" "${TESTDIR}/test2.txt"
|
||||
|
||||
assert_success
|
||||
assert_output --regexp "\[ info\] symlink /.*/test\.txt → /.*/test2\.txt"
|
||||
assert_link_exist "test2.txt"
|
||||
assert_file_not_exist "test2.txt.bak"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_testParseYAML_() {
|
||||
|
||||
@test "_parseYAML: success" {
|
||||
run _parseYAML_ "$YAML1"
|
||||
assert_success
|
||||
assert_output "$( cat "$YAML1parse")"
|
||||
}
|
||||
|
||||
@test "_parseYAML_: empty file" {
|
||||
touch empty.yaml
|
||||
run _parseYAML_ "empty.yaml"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "_parseYAML_: no file" {
|
||||
run _parseYAML_ "empty.yaml"
|
||||
assert_failure
|
||||
}
|
||||
}
|
||||
|
||||
@test "_readFile_: Failure" {
|
||||
run _readFile_ "testfile.txt"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "_readFile_: Reads files line by line" {
|
||||
echo -e "line 1\nline 2\nline 3" > testfile.txt
|
||||
|
||||
run _readFile_ "testfile.txt"
|
||||
assert_line --index 0 'line 1'
|
||||
assert_line --index 2 'line 3'
|
||||
}
|
||||
|
||||
@test "_sourceFile_ failure" {
|
||||
run _sourceFile_ "someNonExistantFile"
|
||||
|
||||
assert_failure
|
||||
assert_output --partial "[ fatal] Attempted to source 'someNonExistantFile'. Not found"
|
||||
}
|
||||
|
||||
@test "_sourceFile_ success" {
|
||||
echo "echo 'hello world'" > "testSourceFile.txt"
|
||||
run _sourceFile_ "testSourceFile.txt"
|
||||
|
||||
assert_success
|
||||
assert_output "hello world"
|
||||
}
|
||||
|
||||
@test "_uniqueFileName_: Count to 3" {
|
||||
touch "test.txt"
|
||||
touch "test.txt.1"
|
||||
touch "test.txt.2"
|
||||
|
||||
run _uniqueFileName_ "test.txt"
|
||||
assert_output --regexp ".*/test\.txt\.3$"
|
||||
}
|
||||
|
||||
@test "_uniqueFileName_: Don't confuse existing numbers" {
|
||||
touch "test-2.txt"
|
||||
|
||||
run _uniqueFileName_ "test-2.txt"
|
||||
assert_output --regexp ".*/test-2\.txt\.1$"
|
||||
}
|
||||
|
||||
@test "_uniqueFileName_: User specified separator" {
|
||||
touch "test.txt"
|
||||
|
||||
run _uniqueFileName_ "test.txt" " "
|
||||
assert_output --regexp ".*/test\.txt 1$"
|
||||
}
|
||||
|
||||
@test "_uniqueFileName_: failure" {
|
||||
run _uniqueFileName_
|
||||
|
||||
assert_failure
|
||||
}
|
||||
|
||||
_testBackupFile_
|
||||
_testListFiles_
|
||||
_testParseFilename_
|
||||
_testMakeSymlink_
|
||||
_testParseYAML_
|
||||
|
||||
79
test/fixtures/test.md
vendored
Normal file
79
test/fixtures/test.md
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
# About
|
||||
This repository contains everything needed to bootstrap and configure new Mac computer. Included here are:
|
||||
|
||||
* dotfiles
|
||||
* ~/bin/ scripts
|
||||
* Configuration files
|
||||
* Scripting templates and utilities
|
||||
* `install.sh`, a script to put everything where it needs to go
|
||||
|
||||
**Disclaimer:** *I am not a professional programmer and I bear no responsibility whatsoever if any of these scripts wipes your computer, destroys your data, crashes your car, or otherwise causes mayhem and destruction. USE AT YOUR OWN RISK.*
|
||||
|
||||
## install.sh
|
||||
This script runs through a series of tasks to configure a new computer. There are three distinct areas of `install.sh` which are executed in order. These are:
|
||||
|
||||
1. **Bootstrapping** - Installing base components such as Command Line Tools, Homebrew, Node, RVM, etc.
|
||||
2. **Installation** - Symlinking dotfiles and installing executables such as NPM Packages, Homebrew Casks, etc.
|
||||
3. **Configuration** - Configures installed packages and apps.
|
||||
|
||||
The files are organized into three subdirectories.
|
||||
|
||||
```
|
||||
dotfiles
|
||||
├── bin/
|
||||
├── config/
|
||||
│ ├── bash/
|
||||
│ └── shell/
|
||||
├── install.sh
|
||||
├── install-config.yaml
|
||||
├── lib/
|
||||
│ ├── bootstrap/
|
||||
│ └── configure/
|
||||
└── scripting/
|
||||
```
|
||||
|
||||
* **bin** - Symlinked to `~/bin` and is added to your `$PATH`.
|
||||
* **config** - Contains the elements needed to configure your environment and specific apps.
|
||||
* config/**bash** - Files in this directory are *sourced* by `.bash_profile`.
|
||||
* config/**shell** - Files here are symlinked to your local environment. Ahem, dotfiles.
|
||||
* **lib** - Contains the scripts and configuration for `install.sh`
|
||||
* lib/**bootstrap** - Scripts here are executed by `install.sh` first.
|
||||
* lib/**configure** - Scripts here are exectuted by `install.sh` after packages have been installed
|
||||
* **config-install.yaml** - This YAML file contains the list of symlinks to be created, as well as the packages to be installed.
|
||||
* **scripting** - This directory contains bash scripting utilities and templates which I re-use often.
|
||||
|
||||
**IMPORTANT:** Unless you want to use my defaults, make sure you do the following:
|
||||
|
||||
* Edit `config-install.yaml` to reflect your preferred packages
|
||||
* Review the files in `config/` to configure your own aliases, preferences, etc.
|
||||
|
||||
#### Private Files
|
||||
|
||||
Sometimes there are files which contain private information. These might be API keys, local directory structures, or anything else you want to keep hidden.
|
||||
|
||||
Private files are held in a separate folder named `private`. This repository is added as a git-submodule and files within it are symlinked to `$HOME` or sourced to the Bash terminal.
|
||||
|
||||
Since you're not me, you should **fork this repository and replace the `private` directory with a git submodule of your own.**
|
||||
|
||||
Within the private directory you can write your own install script to configure and install your own files. This script should be named: `privateInstall.sh`
|
||||
|
||||
If `private/privateInstall.sh` exists, `install.sh` will invoke it.
|
||||
|
||||
## Cloning this repo to a new computer
|
||||
The first step needed to use these dotfiles is to clone this repo into the $HOME directory. To make this easy, I created [a gist](https://gist.github.com/natelandau/b6ec165862277f3a7a4beff76da53a9c) which can easily be run with the following command:
|
||||
|
||||
```
|
||||
curl -SL https://gist.githubusercontent.com/natelandau/b3e1dfba7491137f0a0f5e25721fffc2/raw/d98763695a0ddef1de9db2383f43149005423f20/bootstrapNewMac | bash
|
||||
```
|
||||
|
||||
This gist creates a script `~/bootstrap.sh` in your home directory which completes the following tasks
|
||||
|
||||
1. Creates a new public SSH key if needed
|
||||
2. Copies your public key to your clipboard
|
||||
3. Opens Github to allow you to add this public key to your 'known keys'
|
||||
4. Clones this dotfiles repo to your home directory
|
||||
|
||||
See. Easy. Now you're ready to run `~/dotfiles/install.sh` and get your new computer working.
|
||||
|
||||
### A Note on Code Reuse
|
||||
Many of the scripts, configuration files, and other information herein were created by me over many years without ever having the intention to make them public. As a novice programmer, I have Googled, GitHubbed, and StackExchanged a path to solve my own scripting needs. Quite often I would lift a function whole-cloth from a GitHub repo and not keep track of it's original location. I have done my best within these files to recreate my footsteps and give credit to the original creators of the code when possible. Unfortunately, I fear that I missed as many as I found. My goal of making this repository public is not to take credit for the wonderful code written by others. If you recognize or wrote something here that I didn't credit, please let me know.
|
||||
BIN
test/fixtures/test.md.enc
vendored
Normal file
BIN
test/fixtures/test.md.enc
vendored
Normal file
Binary file not shown.
29
test/fixtures/yaml1.yaml
vendored
Normal file
29
test/fixtures/yaml1.yaml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# A list of tasty fruits
|
||||
fruits:
|
||||
- Apple
|
||||
- Orange
|
||||
- Strawberry
|
||||
- Mango
|
||||
|
||||
# An single record
|
||||
employee:
|
||||
name: Jimmy veloper
|
||||
job: Developer
|
||||
skill: Elite
|
||||
|
||||
# Multiple records
|
||||
employees:
|
||||
martin:
|
||||
name: Martin D'vloper
|
||||
job: Developer
|
||||
skills:
|
||||
- python
|
||||
- perl
|
||||
- pascal
|
||||
tabitha:
|
||||
name: Tabitha Bitumen
|
||||
job: Developer
|
||||
skills:
|
||||
- lisp
|
||||
- fortran
|
||||
- erlang
|
||||
17
test/fixtures/yaml1.yaml.txt
vendored
Normal file
17
test/fixtures/yaml1.yaml.txt
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
fruits+=("Apple")
|
||||
fruits+=("Orange")
|
||||
fruits+=("Strawberry")
|
||||
fruits+=("Mango")
|
||||
employee_name=("Jimmy veloper")
|
||||
employee_job=("Developer")
|
||||
employee_skill=("Elite")
|
||||
employees_martin_name=("Martin D'vloper")
|
||||
employees_martin_job=("Developer")
|
||||
employees_martin_skills+=("python")
|
||||
employees_martin_skills+=("perl")
|
||||
employees_martin_skills+=("pascal")
|
||||
employees_tabitha_name=("Tabitha Bitumen")
|
||||
employees_tabitha_job=("Developer")
|
||||
employees_tabitha_skills+=("lisp")
|
||||
employees_tabitha_skills+=("fortran")
|
||||
employees_tabitha_skills+=("erlang")
|
||||
@@ -512,7 +512,7 @@ _sourceFile_() {
|
||||
|
||||
[ ! -f "$c" ] \
|
||||
&& {
|
||||
fatal "Attempted to source '$c' Not found"
|
||||
fatal "Attempted to source '$c'. Not found"
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user