Skip to content

Commit

Permalink
Merge pull request #46 from finestructure/coverage
Browse files Browse the repository at this point in the history
Coverage & pipeline improvements
  • Loading branch information
finestructure authored Apr 5, 2019
2 parents 979a599 + e348262 commit 70f774f
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 151 deletions.
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()

}

0 comments on commit 70f774f

Please sign in to comment.