leantesting 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +9 -0
  3. data/LICENSE +10 -0
  4. data/README.md +348 -0
  5. data/Rakefile +9 -0
  6. data/leantesting.gemspec +20 -0
  7. data/lib/BaseClass/APIRequest.rb +195 -0
  8. data/lib/BaseClass/Entity.rb +33 -0
  9. data/lib/BaseClass/EntityHandler.rb +183 -0
  10. data/lib/BaseClass/EntityList.rb +188 -0
  11. data/lib/Entity/Bug/Bug.rb +11 -0
  12. data/lib/Entity/Bug/BugAttachment.rb +7 -0
  13. data/lib/Entity/Bug/BugComment.rb +7 -0
  14. data/lib/Entity/Platform/PlatformBrowser.rb +10 -0
  15. data/lib/Entity/Platform/PlatformBrowserVersion.rb +7 -0
  16. data/lib/Entity/Platform/PlatformDevice.rb +7 -0
  17. data/lib/Entity/Platform/PlatformOS.rb +10 -0
  18. data/lib/Entity/Platform/PlatformOSVersion.rb +7 -0
  19. data/lib/Entity/Platform/PlatformType.rb +10 -0
  20. data/lib/Entity/Project/Project.rb +27 -0
  21. data/lib/Entity/Project/ProjectBugScheme.rb +7 -0
  22. data/lib/Entity/Project/ProjectSection.rb +7 -0
  23. data/lib/Entity/Project/ProjectUser.rb +7 -0
  24. data/lib/Entity/Project/ProjectVersion.rb +7 -0
  25. data/lib/Entity/User/UserOrganization.rb +7 -0
  26. data/lib/Exception/BaseException/SDKException.rb +19 -0
  27. data/lib/Exception/SDKBadJSONResponseException.rb +15 -0
  28. data/lib/Exception/SDKDuplicateRequestException.rb +19 -0
  29. data/lib/Exception/SDKErrorResponseException.rb +13 -0
  30. data/lib/Exception/SDKIncompleteRequestException.rb +19 -0
  31. data/lib/Exception/SDKInvalidArgException.rb +15 -0
  32. data/lib/Exception/SDKMissingArgException.rb +15 -0
  33. data/lib/Exception/SDKUnexpectedResponseException.rb +15 -0
  34. data/lib/Exception/SDKUnsupportedRequestException.rb +19 -0
  35. data/lib/Handler/Attachment/AttachmentsHandler.rb +17 -0
  36. data/lib/Handler/Auth/OAuth2Handler.rb +118 -0
  37. data/lib/Handler/Bug/BugAttachmentsHandler.rb +51 -0
  38. data/lib/Handler/Bug/BugCommentsHandler.rb +20 -0
  39. data/lib/Handler/Bug/BugsHandler.rb +57 -0
  40. data/lib/Handler/Platform/PlatformBrowserVersionsHandler.rb +20 -0
  41. data/lib/Handler/Platform/PlatformBrowsersHandler.rb +23 -0
  42. data/lib/Handler/Platform/PlatformDevicesHandler.rb +10 -0
  43. data/lib/Handler/Platform/PlatformHandler.rb +22 -0
  44. data/lib/Handler/Platform/PlatformOSHandler.rb +23 -0
  45. data/lib/Handler/Platform/PlatformOSVersionsHandler.rb +20 -0
  46. data/lib/Handler/Platform/PlatformTypeDevicesHandler.rb +20 -0
  47. data/lib/Handler/Platform/PlatformTypesHandler.rb +21 -0
  48. data/lib/Handler/Project/ProjectBugReproducibilitySchemeHandler.rb +20 -0
  49. data/lib/Handler/Project/ProjectBugSeveritySchemeHandler.rb +20 -0
  50. data/lib/Handler/Project/ProjectBugStatusSchemeHandler.rb +20 -0
  51. data/lib/Handler/Project/ProjectBugTypeSchemeHandler.rb +20 -0
  52. data/lib/Handler/Project/ProjectBugsHandler.rb +67 -0
  53. data/lib/Handler/Project/ProjectSectionsHandler.rb +39 -0
  54. data/lib/Handler/Project/ProjectUsersHandler.rb +20 -0
  55. data/lib/Handler/Project/ProjectVersionsHandler.rb +39 -0
  56. data/lib/Handler/Project/ProjectsHandler.rb +46 -0
  57. data/lib/Handler/User/UserHandler.rb +14 -0
  58. data/lib/Handler/User/UserOrganizationsHandler.rb +14 -0
  59. data/lib/leantesting.rb +65 -0
  60. data/lib/loader.rb +18 -0
  61. data/tests/APIRequestTest.rb +152 -0
  62. data/tests/BaseClassesTest.rb +96 -0
  63. data/tests/ClientTest.rb +52 -0
  64. data/tests/EntitiesTest.rb +97 -0
  65. data/tests/EntityListTest.rb +51 -0
  66. data/tests/ExceptionsTest.rb +70 -0
  67. data/tests/HandlersRequestsTest.rb +215 -0
  68. data/tests/MockRequestsTest.rb +556 -0
  69. data/tests/OAuth2HandlerTest.rb +75 -0
  70. data/tests/res/upload_sample.jpg +0 -0
  71. metadata +169 -0
@@ -0,0 +1,33 @@
1
+ #
2
+ # Represents a single Entity. All remote responses are decoded and parsed into one or more Entities.
3
+ #
4
+
5
+ class Entity
6
+ attr_accessor :data
7
+
8
+ @origin = nil # Reference to originating Client instance
9
+ @data = nil # Internal entity object data
10
+
11
+ #
12
+ # Constructs an Entity instance
13
+ #
14
+ # Arguments:
15
+ # origin Client -- Original client instance reference
16
+ # data Hash -- Data to be contained in the new Entity. Must be non-empty.
17
+ #
18
+ # Exceptions:
19
+ # SDKInvalidArgException if provided data param is not a hash.
20
+ # SDKInvalidArgException if provided data param is empty. Entities cannot be empty.
21
+ #
22
+ def initialize(origin, data)
23
+ if !data.is_a? Hash
24
+ raise SDKInvalidArgException, '`data` must be a hash'
25
+ elsif data.length.zero?
26
+ raise SDKInvalidArgException, '`data` must be non-empty'
27
+ end
28
+
29
+ @origin = origin
30
+ @data = data
31
+ end
32
+
33
+ end
@@ -0,0 +1,183 @@
1
+ #
2
+ # An EntityHandler is the equivalent of a method centralizer for a corresponding endpoint (such as /v1/entities).
3
+ #
4
+ # Functional naming conventions and equivalents:
5
+ # create(fields) <=> `Create a new Entity`
6
+ # all(fields) <=> `List all Entities`
7
+ # find(id) <=> `Retrieve an existing Entity`
8
+ # delete(id) <=> `Delete an Entity`
9
+ # update(id, fields) <=> `Update an Entity`
10
+ #
11
+ class EntityHandler
12
+
13
+ #
14
+ # Constructs an EntityHandler instance
15
+ #
16
+ # Keyword arguments:
17
+ # origin Client -- Originating client reference
18
+ #
19
+ def initialize(origin)
20
+ @origin = origin # Reference to originating Client instance
21
+ end
22
+
23
+ #
24
+ # Function definition for creating a new entity. Base function checks for invalid parameters.
25
+ #
26
+ # Keyword arguments:
27
+ # fields Hash -- Non-empty hash consisting of entity data to send for adding
28
+ #
29
+ # Exceptions:
30
+ # SDKInvalidArgException if provided fields param is not a hash.
31
+ # SDKInvalidArgException if provided fields param is empty.
32
+ #
33
+ def create(fields)
34
+ if !fields.is_a? Hash
35
+ raise SDKInvalidArgException, '`fields` must be a hash'
36
+ elsif fields.length.zero?
37
+ raise SDKInvalidArgException, '`fields` must be non-empty'
38
+ end
39
+ end
40
+
41
+ #
42
+ # Function definition for listing all entities. Base function checks for invalid parameters.
43
+ #
44
+ # Keyword arguments:
45
+ # filters Hash -- (optional) Filters to apply to restrict listing. Currently supported: limit, page
46
+ #
47
+ # Exceptions:
48
+ # SDKInvalidArgException if provided filters param is not a hash.
49
+ # SDKInvalidArgException if invalid filter value found in filters hash.
50
+ #
51
+ def all(filters = nil)
52
+ if !filters
53
+ filters = {}
54
+ end
55
+
56
+ if !filters.is_a? Hash
57
+ raise SDKInvalidArgException, '`filters` must be a hash'
58
+ else
59
+ filters.each do |k,|
60
+ if !['include', 'limit', 'page'].include? k
61
+ raise SDKInvalidArgException, 'unsupported ' + k + ' for `filters`'
62
+ end
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ #
69
+ # Function definition for retrieving an existing entity. Base function checks for invalid parameters.
70
+ #
71
+ # Keyword arguments:
72
+ # id Fixnum -- ID field to look for in the entity collection
73
+ #
74
+ # Exceptions:
75
+ # SDKInvalidArgException if provided id param is not an integer.
76
+ #
77
+ def find(id)
78
+ if !id.is_a? Fixnum
79
+ raise SDKInvalidArgException, '`id` must be of type Fixnum'
80
+ end
81
+ end
82
+
83
+ #
84
+ # Function definition for deleting an existing entity. Base function checks for invalid parameters.
85
+ #
86
+ # Keyword arguments:
87
+ # id Fixnum -- ID field of entity to delete in the entity collection
88
+ #
89
+ # Exceptions:
90
+ # SDKInvalidArgException if provided id param is not an integer.
91
+ #
92
+ def delete(id)
93
+ if !id.is_a? Fixnum
94
+ raise SDKInvalidArgException, '`id` must be of type Fixnum'
95
+ end
96
+ end
97
+
98
+ #
99
+ # Function definition for updating an existing entity. Base function checks for invalid parameters.
100
+ #
101
+ # Keyword arguments:
102
+ # id Fixnum -- ID field of entity to update in the entity collection
103
+ # fields Hash -- Non-empty dictionary consisting of entity data to send for update
104
+ #
105
+ # Exceptions:
106
+ # SDKInvalidArgException if provided id param is not an integer.
107
+ # SDKInvalidArgException if provided fields param is not a hash.
108
+ # SDKInvalidArgException if provided fields param is empty.
109
+ #
110
+ def update(id, fields)
111
+ if !id.is_a? Fixnum
112
+ raise SDKInvalidArgException, '`id` must be of type Fixnum'
113
+ elsif !fields.is_a? Hash
114
+ raise SDKInvalidArgException, '`fields` must be a hash'
115
+ elsif fields.length.zero?
116
+ raise SDKInvalidArgException, '`fields` must be non-empty'
117
+ end
118
+ end
119
+
120
+ private
121
+
122
+ #
123
+ # Helper function that enforces a structure based on a supported table:
124
+ # - Forces use of REQUIRED fields
125
+ # - Detects duplicate fields
126
+ # - Detects unsupported fields
127
+ #
128
+ # Keyword arguments:
129
+ # obj Hash -- Hash to be enforced
130
+ # supports Hash -- Support table consisting of REQUIRED and OPTIONAL keys to be used in enforcing
131
+ #
132
+ # Exceptions:
133
+ # SDKUnsupportedRequestException if unsupported fields are found
134
+ # SDKIncompleteRequestException if any required field is missing
135
+ # SDKDuplicateRequestException if any duplicate field is found
136
+ #
137
+ def enforce(obj, supports)
138
+ sall = [] # All supported keys
139
+ sreq = [] # Mandatory supported keys
140
+
141
+ socc = supports.clone # Key use occurances
142
+
143
+ unsup = [] # Unsupported key list
144
+ dupl = [] # Duplicate key list
145
+ mreq = [] # Missing required keys
146
+
147
+ supports.each do |sk, sv|
148
+ if sv == true
149
+ sreq.push(sk)
150
+ end
151
+
152
+ sall.push(sk)
153
+ socc[sk] = 0
154
+ end
155
+
156
+ obj.each do |k,|
157
+ if sall.include? k
158
+ socc[k] += 1
159
+ else
160
+ unsup.push(k)
161
+ end
162
+ end
163
+
164
+ socc.each do |ok, ov|
165
+ if ov > 1
166
+ dupl.push(ok)
167
+ elsif ov == 0 && sreq.include?(ok)
168
+ mreq.push(ok)
169
+ end
170
+ end
171
+
172
+ if unsup.length.nonzero?
173
+ raise SDKUnsupportedRequestException, unsup
174
+ elsif mreq.length.nonzero?
175
+ raise SDKIncompleteRequestException, mreq
176
+ elsif dupl.length.nonzero?
177
+ raise SDKDuplicateRequestException, dupl
178
+ end
179
+
180
+ true
181
+ end
182
+
183
+ end
@@ -0,0 +1,188 @@
1
+ #
2
+ # An EntityList is a list of Entity objects, obtained from compiling the results of an all() call.
3
+ #
4
+ class EntityList
5
+ attr_reader :collection
6
+
7
+ @origin = nil # Reference to originating Client instance
8
+
9
+ @identifier = nil # Class definition identifier for the collection Entities
10
+ @collection = nil # Internal collection corresponding to current page
11
+
12
+ @request = nil # APIRequest definition to use for collection generation
13
+ @filters = nil # Filter list for generation (origins in Handler call)
14
+
15
+ @pagination = nil # Pagination object as per response (without links)
16
+
17
+ #
18
+ # Constructs an Entity List instance.
19
+ #
20
+ # Arguments:
21
+ # origin Client -- Original client instance reference
22
+ # request APIRequest -- An API Request definition given by the entity collection handler. This is used for any
23
+ # subsequent collection regeneration, as any data updates are dependant on external requests.
24
+ # identifier Class -- class definition to use for dynamic class instancing within array collection
25
+ # filters Hash -- original filters passed over from originating all() call
26
+ #
27
+ def initialize(origin, request, identifier, filters = nil)
28
+ if !filters
29
+ filters = {}
30
+ end
31
+
32
+ @origin = origin
33
+ @request = request
34
+ @identifier = identifier
35
+ @filters = filters
36
+
37
+ generateCollectionData
38
+
39
+ end
40
+
41
+ #
42
+ # Sets iterator position to first page. Ignored if already on first page.
43
+ #
44
+ def first
45
+ if @pagination['current_page'] == 1
46
+ return false
47
+ end
48
+
49
+ @filters['page'] = 1
50
+ generateCollectionData
51
+ end
52
+
53
+ #
54
+ # Sets iterator position to previous page. Ignored if on first page.
55
+ #
56
+ def previous
57
+ if @pagination['current_page'] == 1
58
+ return false
59
+ end
60
+
61
+ @filters['page'] -=1
62
+ generateCollectionData
63
+ end
64
+
65
+ #
66
+ # Sets iterator position to next page. Ignored if on last page.
67
+ #
68
+ def next
69
+ if @pagination['current_page'] == @pagination['total_pages']
70
+ return false
71
+ end
72
+
73
+ if @filters.has_key? 'page'
74
+ @filters['page'] += 1
75
+ else
76
+ @filters['page'] = 2
77
+ end
78
+
79
+ generateCollectionData
80
+ end
81
+
82
+ #
83
+ # Sets iterator position to last page. Ignored if already on last page.
84
+ #
85
+ def last
86
+ if @pagination['current_page'] == @pagination['total_pages']
87
+ return false
88
+ end
89
+
90
+ @filters['page'] = @pagination['total_pages']
91
+ generateCollectionData
92
+ end
93
+
94
+ #
95
+ # Internal loop handler for emulating enumerable functionality
96
+ #
97
+ def each
98
+ first
99
+ begin
100
+ yield toArray
101
+ end while self.next
102
+ end
103
+
104
+ #
105
+ # Outputs total number of Entities inside multi-page collection
106
+ #
107
+ # Returns:
108
+ # Fixnum -- Number of total Entities
109
+ #
110
+ def total
111
+ @pagination['total']
112
+ end
113
+
114
+ #
115
+ # Outputs total number of pages the multi-page collection has, regardful of limit/per_page
116
+ #
117
+ # Returns:
118
+ # Fixnum -- Number of total pages
119
+ #
120
+ def totalPages
121
+ @pagination['total_pages']
122
+ end
123
+
124
+ #
125
+ # Outputs number of Entities in current collection page. Will always be same as limmit/per_page if not on last page.
126
+ #
127
+ # Returns:
128
+ # Fixnum -- Number of Entities in page
129
+ #
130
+ def count
131
+ @pagination['count']
132
+ end
133
+
134
+ #
135
+ # Outputs internal collection in array format (converted from Entity objects)
136
+ #
137
+ # Returns:
138
+ # Array -- array of elements converted into hashes
139
+ #
140
+ def toArray
141
+ @collection.map{ |entity| entity.data }
142
+ end
143
+
144
+ private
145
+
146
+ #
147
+ # (Re)generates internal collection data based on current iteration position.
148
+ #
149
+ # Regeneration is done every time position changes (i.e. every time repositioning functions are used).
150
+ #
151
+ # Exceptions:
152
+ # SDKUnexpectedResponseException if no `meta` field is found
153
+ # SDKUnexpectedResponseException if no `pagination` field is found in `meta field`
154
+ # SDKUnexpectedResponseException if no collection set is found
155
+ # SDKUnexpectedResponseException if multiple collection sets are found
156
+ #
157
+ def generateCollectionData
158
+ @collection = [] # Clear previous collection data on fresh regeneration
159
+ @pagination = {} # Clear previous pagination data on fresh regeneration
160
+
161
+ @request.updateOpts({'params' => @filters})
162
+ raw = @request.exec
163
+
164
+ if !raw.has_key? 'meta'
165
+ raise SDKUnexpectedResponseException, 'missing `meta` field'
166
+ elsif !raw['meta'].has_key? 'pagination'
167
+ raise SDKUnexpectedResponseException, '`meta` missing `pagination` field'
168
+ end
169
+
170
+ if raw['meta']['pagination'].has_key? 'links'
171
+ raw['meta']['pagination'].delete('links') # Remove not needed links sub-data
172
+ end
173
+
174
+ @pagination = raw['meta']['pagination'] # Pass pagination data as per response meta key
175
+ raw.delete('meta')
176
+
177
+ if raw.length.zero?
178
+ raise SDKUnexpectedResponseException, 'collection object missing'
179
+ elsif raw.length > 1
180
+ cols = raw.map{ |k,| k }.join(', ')
181
+ raise SDKUnexpectedResponseException, 'expected one collection object, multiple received: ' + cols
182
+ end
183
+
184
+ classDef = @identifier # Definition to be used for dynamic Entity instancing
185
+ raw.values[0].each{ |entity| @collection.push(classDef.new(@origin, entity)) }
186
+ end
187
+
188
+ end
@@ -0,0 +1,11 @@
1
+ class Bug < Entity
2
+ attr_reader :comments, :attachments
3
+
4
+ def initialize(origin, data)
5
+ super
6
+
7
+ @comments = BugCommentsHandler.new(origin, data['id'])
8
+ @attachments = BugAttachmentsHandler.new(origin, data['id'])
9
+ end
10
+
11
+ end
@@ -0,0 +1,7 @@
1
+ class BugAttachment < Entity
2
+
3
+ def initialize(origin, data)
4
+ super
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class BugComment < Entity
2
+
3
+ def initialize(origin, data)
4
+ super
5
+ end
6
+
7
+ end
@@ -0,0 +1,10 @@
1
+ class PlatformBrowser < Entity
2
+ attr_reader :versions
3
+
4
+ def initialize(origin, data)
5
+ super
6
+
7
+ @versions = PlatformBrowserVersionsHandler.new(origin, data['id'])
8
+ end
9
+
10
+ end
@@ -0,0 +1,7 @@
1
+ class PlatformBrowserVersion < Entity
2
+
3
+ def initialize(origin, data)
4
+ super
5
+ end
6
+
7
+ end