Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Coverage #46

Merged
merged 13 commits into from
Apr 5, 2019
61 changes: 25 additions & 36 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,45 @@ env:
- secure: "j39yC3GJMPK1/s/Q3VLT7fLuwhkN9lmo9OcRVb5iP8OW7HPrxb4b/e7Yp5Gia2KBjHVai/1zH0YA8UFzQju4VjvZdgLc1F+I7FK4NCXG2Vib3fy76A2wOvNl29qrTMgjYjA6dsfdUigLtZf0EE0yLV8MeVGVzsOa4w2iiJhZtWoNbBY7Xo/cAm9csZvvMJ7W+03tmsWo0VLwV9b1F8gW+WXWNLjg84tOj4KsGPDd+1dniK5xb2OxmmzygcxULm5Sbw/Dityz963LFGHIXlVxPzlQxFnio7iOQn60SA12+dwmRfRWHdUTWp7/pQgvK69kdTCJ2D8K5/QTNuxEJQ+dAT8OEoGISYUa9wS88bHM4NqK+uA9t5XsLkrpSyHMCvWadeacwMyIqxGcc9IwHVJYpMwFX2+ufu3g3JwXV54w+jjj72N/shMvhKQVfMM1V+zUGKyblPMYfbFizywa96/R4UTQFjvBknC95SnsQPOqCEEaiBKYVGtHWiFxXIsiX07lEsbsrbdzczFxBJCgYLPSpkVuydWkXxjpXZ3mo47lZomJy354RW3nRs4pfmz99o5IVjeLPqUiElXA9tkFu4yzRB5L1K+LUxwnLy1SX79BvA06rQBkqXRt5TBidtow3LcxpfrqwP0Eoop6GnoiPhbez3ZUnp3hgskAZovqCKaPzsc="

stages:
- build base image
- build app image
- test


install:
- if [[ $TRAVIS_OS_NAME == "linux" ]]; then
eval "$(curl -sL https://gist.githubusercontent.com/kylef/5c0475ff02b7c7671d2a/raw/9f442512a46d7a2af7b850d65a7e9bd31edfb09b/swiftenv-install.sh)";
fi
- deploy


jobs:
include:
- os: linux
dist: trusty
- os: osx

- name: macos spm test
stage: test
os: osx
osx_image: xcode10.2
script: make test-macos-spm

- stage: build base image
- name: macos xcode test
stage: test
os: osx
osx_image: xcode10.2
script: make test-macos-xcode
after_success:
# upload coverage data
- bash <(curl -s https://codecov.io/bash) -J '^ResterCore$' -D .build/derivedData

- name: docker spm test
stage: test
os: linux
dist: trusty
script: |
set -e

# login
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin

# build
VERSION=${TRAVIS_TAG:-$TRAVIS_COMMIT}
IMG=finestructure/rester:base-$VERSION
docker build --pull -t $IMG -f Dockerfile.base .

# push
docker push $IMG
IMAGE=finestructure/rester:base-$VERSION
docker build --pull -t $IMAGE -f Dockerfile.base .
docker run --rm -e GITHUB_TOKEN=$GITHUB_TOKEN $IMAGE swift test
docker push $IMAGE

- stage: build app image
- name: push app image
stage: deploy
os: linux
dist: trusty
script: |
set -e

Expand All @@ -64,17 +67,3 @@ jobs:
# tag and push latest
docker tag $IMG finestructure/rester:latest
docker push finestructure/rester:latest

- stage: test
name: swift test
script: swift test

- stage: test
name: docker swift test
os: linux
script: |
set -e
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
VERSION=${TRAVIS_TAG:-$TRAVIS_COMMIT}
IMAGE=finestructure/rester:base-$VERSION
docker run --rm -e GITHUB_TOKEN=$GITHUB_TOKEN $IMAGE swift test
14 changes: 9 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,23 @@ build-docker-app: build-docker-base
docker tag rester-base finestructure/rester:base-$(VERSION)
docker build --tag rester:$(VERSION) -f Dockerfile.app --build-arg VERSION=$(VERSION) .

test-linux: build-docker-base
test-linux-spm: build-docker-base
docker run --rm rester-base swift test

test-macos: xcodeproj
test-macos-xcode: xcodeproj
set -o pipefail && \
xcodebuild test \
-scheme Rester \
-destination platform="macOS" \
-enableCodeCoverage YES \
-derivedDataPath .build/derivedData

test-swift:
swift test
test-macos-spm: BUILD_DIR=$(shell swift build --show-bin-path)
test-macos-spm:
swift test --enable-code-coverage
xcrun llvm-cov report -ignore-filename-regex=".build/*" -instr-profile $(BUILD_DIR)/codecov/default.profdata $(BUILD_DIR)/rester

test-all: test-linux test-macos
test-all: test-linux-spm test-macos-spm test-macos-xcode

magic:
sourcery --templates ./.sourcery --sources Tests --args testimports='@testable import '"ResterTests" --output Tests/LinuxMain.swift
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ let package = Package(
targets: [
.target(
name: "Rester",
dependencies: ["Commander", "Rainbow", "ResterCore"]),
dependencies: ["ResterCore"]),
.target(
name: "ResterCore",
dependencies: ["LegibleError", "PMKFoundation", "Path", "PromiseKit", "Rainbow", "Regex", "ValueCodable", "Yams"]),
dependencies: ["Commander", "LegibleError", "PMKFoundation", "Path", "PromiseKit", "Rainbow", "Regex", "ValueCodable", "Yams"]),
.testTarget(
name: "ResterTests",
dependencies: ["ResterCore", "SnapshotTesting"]),
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Rester

![](https://img.shields.io/badge/Swift-5-blue.svg) [![Build Status](https://travis-ci.org/finestructure/Rester.svg?branch=develop)](https://travis-ci.org/finestructure/Rester)
![](https://img.shields.io/badge/Swift-5-blue.svg) [![Build Status](https://travis-ci.org/finestructure/Rester.svg?branch=develop)](https://travis-ci.org/finestructure/Rester) [![codecov](https://codecov.io/gh/finestructure/Rester/branch/develop/graph/badge.svg)](https://codecov.io/gh/finestructure/Rester)

Rester is a command line tool to test HTTP APIs. It takes a request description like the following:

Expand Down
108 changes: 1 addition & 107 deletions Sources/Rester/main.swift
Original file line number Diff line number Diff line change
@@ -1,109 +1,3 @@
import Commander
import Foundation
import LegibleError
import Path
import PromiseKit
import Rainbow
import ResterCore
import Yams


extension Console {
func display(variables: [Key: Value]) {
guard variables.count > 0 else { return }
Current.console.display(verbose: "Defined variables:")
for v in variables.keys {
Current.console.display(verbose: " - \(v)")
}
Current.console.display(verbose: "")
}
}


func before(name: Request.Name) {
Current.console.display("🎬 \(name.blue) started ...\n")
}


func after(name: Request.Name, response: Response, result: ValidationResult) -> Bool {
switch result {
case .valid:
let duration = format(response.elapsed).map { " (\($0)s)" } ?? ""
Current.console.display("✅ \(name.blue) \("PASSED".green.bold)\(duration)\n")
return true
case let .invalid(message):
Current.console.display(verbose: "Response:".bold)
Current.console.display(verbose: "\(response)\n")
Current.console.display("❌ \(name.blue) \("FAILED".red.bold) : \(message.red)\n")
return false
}
}


let main = command(
Flag("verbose", flag: "v", description: "Verbose output"),
Option<String>("workdir", default: "", flag: "w", description: "Working directory (for the purpose of resolving relative paths in Restfiles)"),
Option<TimeInterval>("timeout", default: 10, flag: "t", description: "Request timeout"),
Flag("insecure", default: false, description: "do not validate SSL certificate (macOS only)"),
Argument<String>("filename", description: "A Restfile")
) { verbose, wdir, timeout, insecure, filename in

#if !os(macOS)
if insecure {
Current.console.display("--insecure flag currently only supported on macOS")
exit(1)
}
#endif

Current.console.display("🚀 Resting \(filename.bold) ...\n")

let restfilePath = Path(filename) ?? Path.cwd/filename
Current.workDir = getWorkDir(input: wdir) ?? (restfilePath).parent

if verbose {
Current.console.display(verbose: "Restfile path: \(restfilePath)")
Current.console.display(verbose: "Working directory: \(Current.workDir)\n")
}

if timeout != Request.defaultTimeout {
Current.console.display(verbose: "Request timeout: \(timeout)s\n")
}

let rester: Rester
do {
rester = try Rester(path: restfilePath, workDir: Current.workDir)
} catch {
Current.console.display(error)
exit(1)
}

if verbose {
Current.console.display(variables: rester.allVariables)
}

guard rester.requestCount > 0 else {
Current.console.display("⚠️ no requests defined in \(filename.bold)!")
exit(0)
}

rester.test(before: before, after: after, timeout: timeout, validateCertificate: !insecure)
.done { results in
let failureCount = results.filter { !$0 }.count
let failureMsg = failureCount == 0 ? "0".green.bold : failureCount.description.red.bold
Current.console.display("Executed \(results.count.description.bold) tests, with \(failureMsg) failures")
if failureCount > 0 {
exit(1)
} else {
exit(0)
}
}.catch { error in
Current.console.display(error)
exit(1)
}

RunLoop.main.run()

}


main.run(ResterVersion)
app.run(ResterVersion)
108 changes: 108 additions & 0 deletions Sources/ResterCore/App.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//
// Main.swift
// ResterCore
//
// Created by Sven A. Schmidt on 05/04/2019.
//

import Commander
import Foundation
import Path


extension Console {
func display(variables: [Key: Value]) {
guard variables.count > 0 else { return }
Current.console.display(verbose: "Defined variables:")
for v in variables.keys {
Current.console.display(verbose: " - \(v)")
}
Current.console.display(verbose: "")
}
}


func before(name: Request.Name) {
Current.console.display("🎬 \(name.blue) started ...\n")
}


func after(name: Request.Name, response: Response, result: ValidationResult) -> Bool {
switch result {
case .valid:
let duration = format(response.elapsed).map { " (\($0)s)" } ?? ""
Current.console.display("✅ \(name.blue) \("PASSED".green.bold)\(duration)\n")
return true
case let .invalid(message):
Current.console.display(verbose: "Response:".bold)
Current.console.display(verbose: "\(response)\n")
Current.console.display("❌ \(name.blue) \("FAILED".red.bold) : \(message.red)\n")
return false
}
}


public let app = command(
Flag("verbose", flag: "v", description: "Verbose output"),
Option<String>("workdir", default: "", flag: "w", description: "Working directory (for the purpose of resolving relative paths in Restfiles)"),
Option<TimeInterval>("timeout", default: 10, flag: "t", description: "Request timeout"),
Flag("insecure", default: false, description: "do not validate SSL certificate (macOS only)"),
Argument<String>("filename", description: "A Restfile")
) { verbose, wdir, timeout, insecure, filename in

#if !os(macOS)
if insecure {
Current.console.display("--insecure flag currently only supported on macOS")
exit(1)
}
#endif

Current.console.display("🚀 Resting \(filename.bold) ...\n")

let restfilePath = Path(filename) ?? Path.cwd/filename
Current.workDir = getWorkDir(input: wdir) ?? (restfilePath).parent

if verbose {
Current.console.display(verbose: "Restfile path: \(restfilePath)")
Current.console.display(verbose: "Working directory: \(Current.workDir)\n")
}

if timeout != Request.defaultTimeout {
Current.console.display(verbose: "Request timeout: \(timeout)s\n")
}

let rester: Rester
do {
rester = try Rester(path: restfilePath, workDir: Current.workDir)
} catch {
Current.console.display(error)
exit(1)
}

if verbose {
Current.console.display(variables: rester.allVariables)
}

guard rester.requestCount > 0 else {
Current.console.display("⚠️ no requests defined in \(filename.bold)!")
exit(0)
}

rester.test(before: before, after: after, timeout: timeout, validateCertificate: !insecure)
.done { results in
let failureCount = results.filter { !$0 }.count
let failureMsg = failureCount == 0 ? "0".green.bold : failureCount.description.red.bold
Current.console.display("Executed \(results.count.description.bold) tests, with \(failureMsg) failures")
if failureCount > 0 {
exit(1)
} else {
exit(0)
}
}.catch { error in
Current.console.display(error)
exit(1)
}

RunLoop.main.run()

}