Skip to content
This repository has been archived by the owner on Dec 26, 2019. It is now read-only.

Commit

Permalink
Merge pull request #24 from ba-st/18-Sensible-parameters
Browse files Browse the repository at this point in the history
Add support for sensitive arguments, so it's real value wouldn't end u…
  • Loading branch information
gcotelli committed Nov 29, 2019
2 parents b1f0ccf + 6f6bdd3 commit d6dc2fa
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ DummyApplicationStarterCommandLineHandler >> configurationDefinition [
^ OrderedCollection new
add: ( MandatoryArgument named: #mandatory );
add: ( OptionalArgument named: 'optional' defaultingTo: 'unused-optional' );
add: ( MandatoryArgument named: 'secret' ) asSensitive;
yourself
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,59 @@ Class {
#name : #DummyApplicationStarterCommandLineHandlerTest,
#superclass : #TestCase,
#instVars : [
'fakeStdout',
'fakeStderr',
'currentLogFileName',
'memoryFileSystem',
'activated'
'activated',
'inMemoryStderr',
'inMemoryStdout'
],
#category : #'Application-Starter-Tests'
}

{ #category : #running }
{ #category : #private }
DummyApplicationStarterCommandLineHandlerTest >> assert: anOutputStream matches: lines [

anOutputStream contents lines
with: lines
do: [ :line :expected | self assert: ( expected match: line ) ]
]

{ #category : #private }
DummyApplicationStarterCommandLineHandlerTest >> assertPreconditions [

self
deny: activated;
assert: inMemoryStdout contents isEmpty;
assert: inMemoryStderr contents isEmpty
]

{ #category : #private }
DummyApplicationStarterCommandLineHandlerTest >> handlerWithAll: arguments evaluating: aBlock [

| handler |

handler := DummyApplicationStarterCommandLineHandler new
commandLine: ( CommandLineArguments withArguments: arguments ).
handler activationBlock: [ activated := true. aBlock value ].
^ handler
]

{ #category : #running }
DummyApplicationStarterCommandLineHandlerTest >> redirectStdoutAndStderrToMemoryDuring: aBlock [

CurrentLogger
value: ( LeveledLogger outputTo: inMemoryStdout writeStream errorsTo: inMemoryStderr writeStream )
during: aBlock
]

{ #category : #running }
DummyApplicationStarterCommandLineHandlerTest >> setUp [

super setUp.
memoryFileSystem := FileSystem memory.
DummyApplicationStarterCommandLineHandler defaultLogsPath: memoryFileSystem / 'logs'.
fakeStdout := memoryFileSystem / 'stdout'.
fakeStderr := memoryFileSystem / 'stderr'.
inMemoryStdout := memoryFileSystem / 'stdout'.
inMemoryStderr := memoryFileSystem / 'stderr'.
currentLogFileName := Smalltalk logFileName.
activated := false
]
Expand All @@ -44,92 +72,84 @@ DummyApplicationStarterCommandLineHandlerTest >> tearDown [
{ #category : #tests }
DummyApplicationStarterCommandLineHandlerTest >> testActivation [

CurrentLogger
value: ( LeveledLogger outputTo: fakeStdout writeStream errorsTo: fakeStderr writeStream )
during: [ | arguments handler |

arguments := CommandLineArguments
withArguments: {'start-service' . '--debug-mode' . '--optional=used-optional' . '--mandatory=something'}.
handler := DummyApplicationStarterCommandLineHandler new commandLine: arguments.
handler activationBlock: [ activated := true ].
self deny: activated.
self assert: fakeStdout contents isEmpty.
self assert: fakeStderr contents isEmpty.
self shouldnt: [ handler activate ] raise: Exit.
self assert: activated.
self assert: ( '/logs/dummy-*.log' match: Smalltalk logFileName ).
self assert: fakeStderr contents isEmpty.
self deny: fakeStdout contents isEmpty.
self
redirectStdoutAndStderrToMemoryDuring: [ | handler |

handler := self
handlerWithAll: #('start-service' '--debug-mode' '--optional=used-optional' '--mandatory=something' '--secret=XXX')
evaluating: [ ].
self
assert: fakeStdout
matches: {'[*] [INFO] mandatory: ''something''' . '[*] [INFO] optional: ''used-optional'''}.
self assert: ( handler configuration at: #optional ) equals: 'used-optional'.
self assert: ( handler configuration at: #mandatory ) equals: 'something'
assertPreconditions;
shouldnt: [ handler activate ] raise: Exit;
assert: activated.

self
assert: ( '/logs/dummy-*.log' match: Smalltalk logFileName );
assert: inMemoryStderr contents isEmpty;
deny: inMemoryStdout contents isEmpty;
assert: inMemoryStdout
matches:
#('[*] [INFO] mandatory: ''something''' '[*] [INFO] optional: ''used-optional''' '[*] [INFO] secret: *********');
assert: ( handler configuration at: #optional ) equals: 'used-optional';
assert: ( handler configuration at: #mandatory ) equals: 'something';
assert: ( handler configuration at: #secret ) equals: 'XXX'
]
]

{ #category : #tests }
DummyApplicationStarterCommandLineHandlerTest >> testActivationWithErrorInDebugMode [

CurrentLogger
value: ( LeveledLogger outputTo: fakeStdout writeStream errorsTo: fakeStderr writeStream )
during: [ | arguments handler |

arguments := CommandLineArguments
withArguments: {'start-service' . '--debug-mode' . '--optional=used-optional' . '--mandatory=something'}.
handler := DummyApplicationStarterCommandLineHandler new commandLine: arguments.
handler
activationBlock: [ activated := true.
ZeroDivide signal
].

self deny: activated.
self assert: fakeStdout contents isEmpty.
self assert: fakeStderr contents isEmpty.
self should: [ handler activate ] raise: ZeroDivide.
self assert: activated.
self assert: ( '/logs/dummy-*.log' match: Smalltalk logFileName ).
self assert: fakeStderr contents isEmpty.
self deny: fakeStdout contents isEmpty.
self
redirectStdoutAndStderrToMemoryDuring: [
| handler |

handler := self
handlerWithAll: #('start-service' '--debug-mode' '--optional=used-optional' '--mandatory=something' '--secret=XXX')
evaluating: [ ZeroDivide signal ].

self
assertPreconditions;
should: [ handler activate ] raise: ZeroDivide;
assert: activated.

self
assert: fakeStdout
matches: {'[*] [INFO] mandatory: ''something''' . '[*] [INFO] optional: ''used-optional'''}.
self assert: ( handler configuration at: #optional ) equals: 'used-optional'.
self assert: ( handler configuration at: #mandatory ) equals: 'something'
assert: ( '/logs/dummy-*.log' match: Smalltalk logFileName );
assert: inMemoryStderr contents isEmpty;
deny: inMemoryStdout contents isEmpty;
assert: inMemoryStdout
matches:
#('[*] [INFO] mandatory: ''something''' '[*] [INFO] optional: ''used-optional''' '[*] [INFO] secret: *********');
assert: ( handler configuration at: #optional ) equals: 'used-optional';
assert: ( handler configuration at: #mandatory ) equals: 'something';
assert: ( handler configuration at: #secret ) equals: 'XXX'
]
]

{ #category : #tests }
DummyApplicationStarterCommandLineHandlerTest >> testActivationWithErrorWithoutDebugMode [

CurrentLogger
value: ( LeveledLogger outputTo: fakeStdout writeStream errorsTo: fakeStderr writeStream )
during: [ | arguments handler |

arguments := CommandLineArguments
withArguments: {'start-service' . '--optional=used-optional' . '--mandatory=something'}.
handler := DummyApplicationStarterCommandLineHandler new commandLine: arguments.
handler
activationBlock: [ activated := true.
ZeroDivide signal: 'Division by Zero'
].
self deny: activated.
self assert: fakeStdout contents isEmpty.
self assert: fakeStderr contents isEmpty.
self should: [ handler activate ] raise: Exit.
self assert: activated.
self assert: ( '/logs/dummy-*.log' match: Smalltalk logFileName ).
self
assert: fakeStderr
matches:
{'[*] [ERROR] Dumping Stack Due to Unexpected Error: Division by Zero'.
'[*] [ERROR] Dumping Stack Due to Unexpected Error: Division by Zero... [OK]'}.
self
redirectStdoutAndStderrToMemoryDuring: [ | handler |

handler := self
handlerWithAll: #('start-service' '--optional=used-optional' '--mandatory=something' '--secret=XXX')
evaluating: [ ZeroDivide signal: 'Division by Zero' ].
self
assert: fakeStdout
matches: {'[*] [INFO] mandatory: ''something''' . '[*] [INFO] optional: ''used-optional'''}.
self assert: ( handler configuration at: #optional ) equals: 'used-optional'.
self assert: ( handler configuration at: #mandatory ) equals: 'something'.
assertPreconditions;
should: [ handler activate ] raise: Exit;
assert: activated.

self
assert: ( '/logs/dummy-*.log' match: Smalltalk logFileName );
assert: inMemoryStderr
matches:
#('[*] [ERROR] Dumping Stack Due to Unexpected Error: Division by Zero' '[*] [ERROR] Dumping Stack Due to Unexpected Error: Division by Zero... [OK]');
assert: inMemoryStdout
matches:
#('[*] [INFO] mandatory: ''something''' '[*] [INFO] optional: ''used-optional''' '[*] [INFO] secret: *********');
assert: ( handler configuration at: #optional ) equals: 'used-optional';
assert: ( handler configuration at: #mandatory ) equals: 'something';
assert: ( handler configuration at: #secret ) equals: 'XXX';
assert:
( ( memoryFileSystem / 'logs' ) childNames
anySatisfy: [ :fileName | 'dummy-*.fuel' match: fileName ] )
Expand All @@ -139,47 +159,40 @@ DummyApplicationStarterCommandLineHandlerTest >> testActivationWithErrorWithoutD
{ #category : #tests }
DummyApplicationStarterCommandLineHandlerTest >> testActivationWithoutMandatoryArguments [

CurrentLogger
value: ( LeveledLogger outputTo: fakeStdout writeStream errorsTo: fakeStderr writeStream )
during: [ | arguments handler |

arguments := CommandLineArguments withArguments: {'start-service' . '--optional=used-optional'}.
handler := DummyApplicationStarterCommandLineHandler new commandLine: arguments.
handler activationBlock: [ activated := true ].
self deny: activated.
self assert: fakeStdout contents isEmpty.
self assert: fakeStderr contents isEmpty.
self should: [ handler activate ] raise: Exit.
self assert: fakeStdout contents isEmpty.
self
redirectStdoutAndStderrToMemoryDuring: [ | handler |

handler := self handlerWithAll: #('start-service' '--optional=used-optional') evaluating: [ ].
self
assert: fakeStderr
matches: {'[*] [ERROR] mandatory option not provided. You must provide one.'}
assertPreconditions;
should: [ handler activate ] raise: Exit;
assert: inMemoryStdout contents isEmpty;
assert: inMemoryStderr
matches: {'[*] [ERROR] mandatory option not provided. You must provide one.'}
]
]

{ #category : #tests }
DummyApplicationStarterCommandLineHandlerTest >> testActivationWithoutOptionalArguments [

CurrentLogger
value: ( LeveledLogger outputTo: fakeStdout writeStream errorsTo: fakeStderr writeStream )
during: [ | arguments handler |

arguments := CommandLineArguments withArguments: {'start-service' . '--mandatory=something'}.
handler := DummyApplicationStarterCommandLineHandler new commandLine: arguments.
handler activationBlock: [ activated := true ].
self deny: activated.
self assert: fakeStdout contents isEmpty.
self assert: fakeStderr contents isEmpty.
self shouldnt: [ handler activate ] raise: Exit.
self assert: activated.
self assert: ( '/logs/dummy-*.log' match: Smalltalk logFileName ).
self
redirectStdoutAndStderrToMemoryDuring: [ | handler |

handler := self
handlerWithAll: #('start-service' '--mandatory=something' '--secret=XXX')
evaluating: [ ].
self
assert: fakeStderr
matches: {'[*] [WARNING] optional option not provided. Defaulting to ''unused-optional'''}.
assertPreconditions;
shouldnt: [ handler activate ] raise: Exit;
assert: activated.
self
assert: fakeStdout
matches: {'[*] [INFO] mandatory: ''something''' . '[*] [INFO] optional: ''unused-optional'''}.
self assert: ( handler configuration at: #optional ) equals: 'unused-optional'.
self assert: ( handler configuration at: #mandatory ) equals: 'something'
assert: ( '/logs/dummy-*.log' match: Smalltalk logFileName );
assert: inMemoryStderr
matches: #('[*] [WARNING] optional option not provided. Defaulting to ''unused-optional''');
assert: inMemoryStdout
matches:
#('[*] [INFO] mandatory: ''something''' '[*] [INFO] optional: ''unused-optional''' '[*] [INFO] secret: *********');
assert: ( handler configuration at: #optional ) equals: 'unused-optional';
assert: ( handler configuration at: #mandatory ) equals: 'something'
]
]
16 changes: 16 additions & 0 deletions source/Application-Starter-Tests/SensitiveArgumentTest.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"
A SensitiveArgumentTest is a test class for testing the behavior of SensitiveArgument
"
Class {
#name : #SensitiveArgumentTest,
#superclass : #TestCase,
#category : #'Application-Starter-Tests'
}

{ #category : #tests }
SensitiveArgumentTest >> testName [

self
assert: ( SensitiveArgument over: ( MandatoryArgument named: 'private' ) ) name
equals: 'private'
]
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,8 @@ ApplicationStarterCommandLineHandler >> logConfiguration [
| configuration |

configuration := self configuration.
configuration keys sorted
do: [ :configurationKey |
CurrentLogger value
logAsInfo:
( '<1s>: <2p>'
expandMacrosWith: configurationKey asString
with: ( configuration at: configurationKey ) )
]
self configurationDefinition
do: [ :definition | definition logIn: CurrentLogger value using: configuration ]
]

{ #category : #activation }
Expand Down
34 changes: 34 additions & 0 deletions source/Application-Starter/CommandLineArgument.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"
A command line argument must provide a command name and way to get the argument value from a command line handler
"
Class {
#name : #CommandLineArgument,
#superclass : #Object,
#category : #'Application-Starter'
}

{ #category : #evaluating }
CommandLineArgument >> argumentFrom: aCommandLineHandler [

^ self subclassResponsibility
]

{ #category : #converting }
CommandLineArgument >> asSensitive [

^ SensitiveArgument over: self
]

{ #category : #logging }
CommandLineArgument >> logIn: aLogger using: configuration [

aLogger
logAsInfo:
( '<1s>: <2p>' expandMacrosWith: self name asString with: ( configuration at: self name ) )
]

{ #category : #accessing }
CommandLineArgument >> name [

^ self subclassResponsibility
]
2 changes: 1 addition & 1 deletion source/Application-Starter/FlagArgument.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The parsed value will be accessible on the handler as
"
Class {
#name : #FlagArgument,
#superclass : #Object,
#superclass : #CommandLineArgument,
#instVars : [
'name',
'valueWhenPresent'
Expand Down
2 changes: 1 addition & 1 deletion source/Application-Starter/MandatoryArgument.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The parsed value will be accessible on the handler as
"
Class {
#name : #MandatoryArgument,
#superclass : #Object,
#superclass : #CommandLineArgument,
#instVars : [
'name',
'converter'
Expand Down
2 changes: 1 addition & 1 deletion source/Application-Starter/OptionalArgument.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The parsed value will be accessible on the handler as
"
Class {
#name : #OptionalArgument,
#superclass : #Object,
#superclass : #CommandLineArgument,
#instVars : [
'name',
'default',
Expand Down

0 comments on commit d6dc2fa

Please sign in to comment.