coupler 0.0.1-java

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 (258) hide show
  1. data/.document +5 -0
  2. data/.gitmodules +3 -0
  3. data/.rvmrc +1 -0
  4. data/.vimrc +40 -0
  5. data/Gemfile +27 -0
  6. data/Gemfile.lock +71 -0
  7. data/LICENSE +20 -0
  8. data/NOTES +6 -0
  9. data/README.rdoc +18 -0
  10. data/Rakefile +42 -0
  11. data/TODO +11 -0
  12. data/VERSION +1 -0
  13. data/bin/coupler +7 -0
  14. data/db/.gitignore +6 -0
  15. data/db/migrate/001_initial_schema.rb +166 -0
  16. data/db/migrate/002_stub.rb +4 -0
  17. data/db/migrate/003_stub.rb +4 -0
  18. data/db/migrate/004_create_comparisons.rb +28 -0
  19. data/db/migrate/005_move_database_name.rb +19 -0
  20. data/db/migrate/006_upgrade_comparisons.rb +34 -0
  21. data/db/migrate/007_add_which_to_comparisons.rb +23 -0
  22. data/db/migrate/008_add_result_field_to_transformations.rb +33 -0
  23. data/db/migrate/009_add_generated_flag_to_fields.rb +13 -0
  24. data/db/migrate/010_create_imports.rb +24 -0
  25. data/db/migrate/011_add_primary_key_type.rb +13 -0
  26. data/db/migrate/012_add_transformed_with_to_resources.rb +13 -0
  27. data/db/migrate/013_add_run_count_to_scenarios.rb +13 -0
  28. data/db/migrate/014_add_last_accessed_at_to_some_tables.rb +13 -0
  29. data/db/migrate/015_add_run_number_to_results.rb +15 -0
  30. data/db/migrate/016_fix_scenario_run_count.rb +27 -0
  31. data/db/migrate/017_rename_comparison_columns.rb +14 -0
  32. data/db/migrate/018_fix_scenario_linkage_type.rb +8 -0
  33. data/db/migrate/019_add_columns_to_imports.rb +24 -0
  34. data/db/migrate/020_rename_import_columns.rb +12 -0
  35. data/db/migrate/021_add_fields_to_connections.rb +15 -0
  36. data/db/migrate/022_remove_database_name_from_resources.rb +11 -0
  37. data/features/connections.feature +28 -0
  38. data/features/matchers.feature +35 -0
  39. data/features/projects.feature +11 -0
  40. data/features/resources.feature +62 -0
  41. data/features/scenarios.feature +45 -0
  42. data/features/step_definitions/coupler_steps.rb +145 -0
  43. data/features/step_definitions/matchers_steps.rb +26 -0
  44. data/features/step_definitions/resources_steps.rb +12 -0
  45. data/features/step_definitions/scenarios_steps.rb +7 -0
  46. data/features/step_definitions/transformations_steps.rb +3 -0
  47. data/features/support/env.rb +128 -0
  48. data/features/transformations.feature +22 -0
  49. data/features/wizard.feature +10 -0
  50. data/gfx/coupler-header.svg +213 -0
  51. data/gfx/coupler-sidebar.svg +656 -0
  52. data/gfx/coupler.svg +184 -0
  53. data/gfx/icon.svg +75 -0
  54. data/lib/coupler/base.rb +63 -0
  55. data/lib/coupler/config.rb +128 -0
  56. data/lib/coupler/data_uploader.rb +20 -0
  57. data/lib/coupler/database.rb +31 -0
  58. data/lib/coupler/extensions/connections.rb +57 -0
  59. data/lib/coupler/extensions/exceptions.rb +58 -0
  60. data/lib/coupler/extensions/imports.rb +43 -0
  61. data/lib/coupler/extensions/jobs.rb +21 -0
  62. data/lib/coupler/extensions/matchers.rb +64 -0
  63. data/lib/coupler/extensions/projects.rb +62 -0
  64. data/lib/coupler/extensions/resources.rb +89 -0
  65. data/lib/coupler/extensions/results.rb +100 -0
  66. data/lib/coupler/extensions/scenarios.rb +50 -0
  67. data/lib/coupler/extensions/transformations.rb +70 -0
  68. data/lib/coupler/extensions/transformers.rb +58 -0
  69. data/lib/coupler/extensions.rb +16 -0
  70. data/lib/coupler/helpers.rb +121 -0
  71. data/lib/coupler/import_buffer.rb +48 -0
  72. data/lib/coupler/logger.rb +16 -0
  73. data/lib/coupler/models/common_model.rb +104 -0
  74. data/lib/coupler/models/comparison.rb +166 -0
  75. data/lib/coupler/models/connection.rb +59 -0
  76. data/lib/coupler/models/field.rb +55 -0
  77. data/lib/coupler/models/import.rb +238 -0
  78. data/lib/coupler/models/job.rb +42 -0
  79. data/lib/coupler/models/jobify.rb +17 -0
  80. data/lib/coupler/models/matcher.rb +36 -0
  81. data/lib/coupler/models/project.rb +40 -0
  82. data/lib/coupler/models/resource.rb +287 -0
  83. data/lib/coupler/models/result.rb +92 -0
  84. data/lib/coupler/models/scenario/runner.rb +357 -0
  85. data/lib/coupler/models/scenario.rb +115 -0
  86. data/lib/coupler/models/transformation.rb +117 -0
  87. data/lib/coupler/models/transformer/runner.rb +28 -0
  88. data/lib/coupler/models/transformer.rb +110 -0
  89. data/lib/coupler/models.rb +30 -0
  90. data/lib/coupler/runner.rb +76 -0
  91. data/lib/coupler/scheduler.rb +56 -0
  92. data/lib/coupler.rb +34 -0
  93. data/log/.gitignore +1 -0
  94. data/misc/README +5 -0
  95. data/misc/jruby-json.license +57 -0
  96. data/misc/rack-flash.license +22 -0
  97. data/script/dbconsole.rb +5 -0
  98. data/src/edu/vanderbilt/coupler/Main.java +116 -0
  99. data/src/edu/vanderbilt/coupler/jruby.properties +1 -0
  100. data/tasks/annotations.rake +84 -0
  101. data/tasks/db.rake +120 -0
  102. data/tasks/environment.rake +12 -0
  103. data/tasks/jeweler.rake +43 -0
  104. data/tasks/package.rake +58 -0
  105. data/tasks/rdoc.rake +13 -0
  106. data/tasks/test.rake +63 -0
  107. data/tasks/vendor.rake +43 -0
  108. data/test/README.txt +6 -0
  109. data/test/config.yml +9 -0
  110. data/test/coupler/models/test_import.rb +221 -0
  111. data/test/factories.rb +91 -0
  112. data/test/fixtures/duplicate-keys.csv +5 -0
  113. data/test/fixtures/no-headers.csv +50 -0
  114. data/test/fixtures/people.csv +51 -0
  115. data/test/fixtures/varying-row-size.csv +4 -0
  116. data/test/helper.rb +156 -0
  117. data/test/integration/extensions/test_connections.rb +80 -0
  118. data/test/integration/extensions/test_imports.rb +94 -0
  119. data/test/integration/extensions/test_jobs.rb +52 -0
  120. data/test/integration/extensions/test_matchers.rb +134 -0
  121. data/test/integration/extensions/test_projects.rb +82 -0
  122. data/test/integration/extensions/test_resources.rb +150 -0
  123. data/test/integration/extensions/test_results.rb +89 -0
  124. data/test/integration/extensions/test_scenarios.rb +88 -0
  125. data/test/integration/extensions/test_transformations.rb +113 -0
  126. data/test/integration/extensions/test_transformers.rb +80 -0
  127. data/test/integration/test_field.rb +45 -0
  128. data/test/integration/test_import.rb +78 -0
  129. data/test/integration/test_running_scenarios.rb +379 -0
  130. data/test/integration/test_transformation.rb +56 -0
  131. data/test/integration/test_transforming.rb +154 -0
  132. data/test/table_sets.rb +76 -0
  133. data/test/unit/models/test_common_model.rb +130 -0
  134. data/test/unit/models/test_comparison.rb +619 -0
  135. data/test/unit/models/test_connection.rb +115 -0
  136. data/test/unit/models/test_field.rb +99 -0
  137. data/test/unit/models/test_import.rb +130 -0
  138. data/test/unit/models/test_job.rb +115 -0
  139. data/test/unit/models/test_matcher.rb +82 -0
  140. data/test/unit/models/test_project.rb +102 -0
  141. data/test/unit/models/test_resource.rb +564 -0
  142. data/test/unit/models/test_result.rb +90 -0
  143. data/test/unit/models/test_scenario.rb +199 -0
  144. data/test/unit/models/test_transformation.rb +193 -0
  145. data/test/unit/models/test_transformer.rb +188 -0
  146. data/test/unit/test_base.rb +60 -0
  147. data/test/unit/test_data_uploader.rb +27 -0
  148. data/test/unit/test_database.rb +23 -0
  149. data/test/unit/test_helpers.rb +58 -0
  150. data/test/unit/test_logger.rb +10 -0
  151. data/test/unit/test_models.rb +12 -0
  152. data/test/unit/test_runner.rb +76 -0
  153. data/test/unit/test_scheduler.rb +66 -0
  154. data/uploads/.gitignore +2 -0
  155. data/vendor/java/.gitignore +5 -0
  156. data/webroot/public/css/960.css +1 -0
  157. data/webroot/public/css/dataTables.css +1057 -0
  158. data/webroot/public/css/jquery-ui.css +572 -0
  159. data/webroot/public/css/jquery.treeview.css +68 -0
  160. data/webroot/public/css/reset.css +1 -0
  161. data/webroot/public/css/style.css +504 -0
  162. data/webroot/public/css/text.css +1 -0
  163. data/webroot/public/favicon.ico +0 -0
  164. data/webroot/public/images/12_col.gif +0 -0
  165. data/webroot/public/images/16_col.gif +0 -0
  166. data/webroot/public/images/add.png +0 -0
  167. data/webroot/public/images/ajax-loader.gif +0 -0
  168. data/webroot/public/images/cog.png +0 -0
  169. data/webroot/public/images/coupler.png +0 -0
  170. data/webroot/public/images/foo.png +0 -0
  171. data/webroot/public/images/hammer.png +0 -0
  172. data/webroot/public/images/header.png +0 -0
  173. data/webroot/public/images/home.gif +0 -0
  174. data/webroot/public/images/jobs.gif +0 -0
  175. data/webroot/public/images/sidebar-bottom.png +0 -0
  176. data/webroot/public/images/sidebar.png +0 -0
  177. data/webroot/public/images/treeview-default-line.gif +0 -0
  178. data/webroot/public/images/treeview-default.gif +0 -0
  179. data/webroot/public/images/ui-anim_basic_16x16.gif +0 -0
  180. data/webroot/public/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  181. data/webroot/public/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  182. data/webroot/public/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  183. data/webroot/public/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  184. data/webroot/public/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  185. data/webroot/public/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  186. data/webroot/public/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  187. data/webroot/public/images/ui-bg_highlight-hard_30_565356_1x100.png +0 -0
  188. data/webroot/public/images/ui-bg_highlight-hard_75_888588_1x100.png +0 -0
  189. data/webroot/public/images/ui-bg_highlight-soft_30_6e3b3a_1x100.png +0 -0
  190. data/webroot/public/images/ui-bg_highlight-soft_35_8e8b8e_1x100.png +0 -0
  191. data/webroot/public/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  192. data/webroot/public/images/ui-icons_222222_256x240.png +0 -0
  193. data/webroot/public/images/ui-icons_2e83ff_256x240.png +0 -0
  194. data/webroot/public/images/ui-icons_454545_256x240.png +0 -0
  195. data/webroot/public/images/ui-icons_888888_256x240.png +0 -0
  196. data/webroot/public/images/ui-icons_cd0a0a_256x240.png +0 -0
  197. data/webroot/public/images/ui-icons_ffffff_256x240.png +0 -0
  198. data/webroot/public/js/ajaxupload.js +673 -0
  199. data/webroot/public/js/application.js +40 -0
  200. data/webroot/public/js/jquery-ui.combobox.js +98 -0
  201. data/webroot/public/js/jquery-ui.js +9867 -0
  202. data/webroot/public/js/jquery-ui.min.js +559 -0
  203. data/webroot/public/js/jquery.dataTables.min.js +587 -0
  204. data/webroot/public/js/jquery.min.js +154 -0
  205. data/webroot/public/js/jquery.timeago.js +140 -0
  206. data/webroot/public/js/jquery.tooltip.min.js +19 -0
  207. data/webroot/public/js/jquery.treeview.min.js +15 -0
  208. data/webroot/public/js/resource.js +11 -0
  209. data/webroot/public/js/results.js +56 -0
  210. data/webroot/public/js/transformations.js +95 -0
  211. data/webroot/views/connections/index.erb +5 -0
  212. data/webroot/views/connections/list.erb +34 -0
  213. data/webroot/views/connections/new.erb +55 -0
  214. data/webroot/views/connections/show.erb +36 -0
  215. data/webroot/views/imports/edit.erb +60 -0
  216. data/webroot/views/imports/form.erb +81 -0
  217. data/webroot/views/imports/new.erb +89 -0
  218. data/webroot/views/index.erb +12 -0
  219. data/webroot/views/jobs/index.erb +7 -0
  220. data/webroot/views/jobs/list.erb +24 -0
  221. data/webroot/views/layout.erb +38 -0
  222. data/webroot/views/matchers/form.erb +250 -0
  223. data/webroot/views/matchers/list.erb +32 -0
  224. data/webroot/views/projects/form.erb +14 -0
  225. data/webroot/views/projects/index.erb +96 -0
  226. data/webroot/views/projects/show.erb +24 -0
  227. data/webroot/views/resources/edit.erb +88 -0
  228. data/webroot/views/resources/index.erb +5 -0
  229. data/webroot/views/resources/list.erb +27 -0
  230. data/webroot/views/resources/new.erb +121 -0
  231. data/webroot/views/resources/show.erb +86 -0
  232. data/webroot/views/resources/transform.erb +2 -0
  233. data/webroot/views/results/csv.erb +12 -0
  234. data/webroot/views/results/details.erb +15 -0
  235. data/webroot/views/results/index.erb +2 -0
  236. data/webroot/views/results/list.erb +22 -0
  237. data/webroot/views/results/record.erb +24 -0
  238. data/webroot/views/results/show.erb +68 -0
  239. data/webroot/views/scenarios/index.erb +5 -0
  240. data/webroot/views/scenarios/list.erb +20 -0
  241. data/webroot/views/scenarios/new.erb +99 -0
  242. data/webroot/views/scenarios/run.erb +2 -0
  243. data/webroot/views/scenarios/show.erb +50 -0
  244. data/webroot/views/sidebar.erb +106 -0
  245. data/webroot/views/transformations/create.erb +115 -0
  246. data/webroot/views/transformations/for.erb +16 -0
  247. data/webroot/views/transformations/index.erb +2 -0
  248. data/webroot/views/transformations/list.erb +29 -0
  249. data/webroot/views/transformations/new.erb +126 -0
  250. data/webroot/views/transformations/preview.erb +46 -0
  251. data/webroot/views/transformers/edit.erb +6 -0
  252. data/webroot/views/transformers/form.erb +58 -0
  253. data/webroot/views/transformers/index.erb +2 -0
  254. data/webroot/views/transformers/list.erb +25 -0
  255. data/webroot/views/transformers/new.erb +5 -0
  256. data/webroot/views/transformers/preview.erb +23 -0
  257. data/webroot/views/transformers/show.erb +0 -0
  258. metadata +558 -0
@@ -0,0 +1,115 @@
1
+ require 'helper'
2
+
3
+ module Coupler
4
+ module Models
5
+ class TestConnection < Coupler::Test::UnitTest
6
+ test "sequel model" do
7
+ assert_equal ::Sequel::Model, Connection.superclass
8
+ assert_equal :connections, Connection.table_name
9
+ end
10
+
11
+ test "one to many resources" do
12
+ assert_respond_to Connection.new, :resources
13
+ end
14
+
15
+ each_adapter do |adapter, _|
16
+ adapter_test(adapter, "requires name") do
17
+ connection = new_connection(adapter, :name => nil)
18
+ assert !connection.valid?
19
+
20
+ connection.name = ""
21
+ assert !connection.valid?
22
+ end
23
+
24
+ adapter_test(adapter, "requires unique name") do
25
+ connection_1 = new_connection(adapter, :name => 'foo')
26
+ connection_1.save!
27
+ connection_2 = new_connection(adapter, :name => 'foo')
28
+ assert !connection_2.valid?
29
+ end
30
+
31
+ adapter_test(adapter, "requires unique name on update") do
32
+ connection_1 = new_connection(adapter, :name => 'foo')
33
+ connection_1.save!
34
+ connection_2 = new_connection(adapter, :name => 'bar')
35
+ connection_2.save!
36
+ connection_1.name = "bar"
37
+ assert !connection_1.valid?, "Connection wasn't invalid"
38
+ end
39
+
40
+ adapter_test(adapter, "sets slug from name") do
41
+ connection = new_connection(adapter, :name => 'Foo bar')
42
+ connection.save!
43
+ assert_equal "foo_bar", connection.slug
44
+ end
45
+
46
+ adapter_test(adapter, "requires unique slug") do
47
+ connection_1 = new_connection(adapter, :name => 'bar', :slug => 'bar')
48
+ connection_1.save!
49
+ connection_2 = new_connection(adapter, :name => 'foo', :slug => 'bar')
50
+ assert !connection_2.valid?
51
+
52
+ connection_2.slug = "baz"
53
+ assert connection_2.valid?
54
+ connection_2.save!
55
+
56
+ connection_2.slug = "bar"
57
+ assert !connection_2.valid?
58
+ end
59
+
60
+ adapter_test(adapter, 'database method') do
61
+ connection = new_connection(adapter, :name => 'test')
62
+ connection.save!
63
+ connection.database do |database|
64
+ assert_kind_of Sequel::JDBC::Database, database
65
+ assert database.test_connection
66
+ end
67
+ end
68
+ end
69
+
70
+ adapter_test('h2', 'requires valid connection') do
71
+ connection = new_connection('h2', :path => '/path/i/cant/create/foo/bar')
72
+ assert !connection.valid?, "Connection wasn't invalid"
73
+ end
74
+
75
+ adapter_test('mysql', 'requires valid connection') do
76
+ connection = new_connection('mysql', :password => 'incorrect_password')
77
+ assert !connection.valid?, "Connection wasn't invalid"
78
+ end
79
+
80
+ test "deletable if unused" do
81
+ connection = new_connection('h2', :name => 'foo')
82
+ connection.save!
83
+ connection.expects(:resources_dataset).returns(stub(:count => 0))
84
+ assert connection.deletable?
85
+ end
86
+
87
+ test "not deletable if associated with resources" do
88
+ connection = new_connection('h2', :name => 'foo')
89
+ connection.save!
90
+ connection.expects(:resources_dataset).returns(stub(:count => 1))
91
+ assert !connection.deletable?
92
+ end
93
+
94
+ test "deleting unused connection" do
95
+ connection = new_connection('h2', :name => 'foo')
96
+ connection.save!
97
+ connection.expects(:resources_dataset).returns(stub(:count => 0))
98
+ assert connection.destroy
99
+ assert_nil Connection[connection.id]
100
+ end
101
+
102
+ test "prevent deleting connection in use" do
103
+ connection = new_connection('h2', :name => 'foo')
104
+ connection.save!
105
+ connection.expects(:resources_dataset).returns(stub(:count => 1))
106
+ assert !connection.destroy
107
+ assert_not_nil Connection[connection.id]
108
+ end
109
+
110
+ #def test_connection_limit
111
+ #pend
112
+ #end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,99 @@
1
+ require 'helper'
2
+
3
+ module Coupler
4
+ module Models
5
+ class TestField < Coupler::Test::UnitTest
6
+ def new_field(attrs = {})
7
+ f = Field.new({
8
+ :name => 'foo',
9
+ :type => 'string',
10
+ :db_type => 'varchar(255)',
11
+ :is_primary_key => false,
12
+ :is_selected => true,
13
+ :resource => @resource
14
+ }.merge(attrs))
15
+ end
16
+
17
+ def setup
18
+ super
19
+ @resource = stub("resource", :id => 123, :pk => 123, :associations => {})
20
+ end
21
+
22
+ test "sequel model" do
23
+ assert_equal ::Sequel::Model, Field.superclass
24
+ assert_equal :fields, Field.table_name
25
+ end
26
+
27
+ test "many to one resource" do
28
+ assert_respond_to Field.new, :resource
29
+ end
30
+
31
+ test "one to many transformations" do
32
+ assert_respond_to Field.new, :transformations
33
+ end
34
+
35
+ test "requires unique name across resources" do
36
+ field = new_field
37
+ field.expects(:validates_unique).with([:name, :resource_id])
38
+ field.valid?
39
+ end
40
+
41
+ test "force selected on primary key fields" do
42
+ field_1 = new_field(:name => 'field_1', :is_primary_key => false, :is_selected => false).save!
43
+ assert !field_1.is_selected
44
+
45
+ field_2 = new_field(:name => 'field_2', :is_primary_key => true, :is_selected => false).save!
46
+ assert field_2.is_selected
47
+ end
48
+
49
+ test "original_column_options" do
50
+ field = new_field({
51
+ :local_type => 'integer',
52
+ :local_db_type => 'int(11)',
53
+ })
54
+ assert_equal({
55
+ :name => 'foo', :type => 'varchar(255)',
56
+ :primary_key => false
57
+ }, field.original_column_options)
58
+ end
59
+
60
+ test "local_column_options" do
61
+ field_1 = new_field({
62
+ :type => 'integer', :db_type => 'int(11)',
63
+ :local_type => 'string', :local_db_type => 'varchar(255)',
64
+ :is_primary_key => false
65
+ })
66
+ field_2 = new_field({
67
+ :type => 'integer', :db_type => 'int(11)',
68
+ :is_primary_key => false
69
+ })
70
+
71
+ assert_equal({
72
+ :name => 'foo', :type => 'varchar(255)',
73
+ :primary_key => false
74
+ }, field_1.local_column_options)
75
+ assert_equal({
76
+ :name => 'foo', :type => 'int(11)',
77
+ :primary_key => false
78
+ }, field_2.local_column_options)
79
+ end
80
+
81
+ test "final_type and final_db_type uses local if exists" do
82
+ field_1 = new_field({
83
+ :name => 'foo', :type => 'integer', :db_type => 'int(11)',
84
+ :local_type => 'string', :local_db_type => 'varchar(255)',
85
+ :is_primary_key => false
86
+ })
87
+ field_2 = new_field({
88
+ :name => 'foo', :type => 'integer', :db_type => 'int(11)',
89
+ :local_type => nil, :local_db_type => nil,
90
+ :is_primary_key => false
91
+ })
92
+ assert_equal 'string', field_1.final_type
93
+ assert_equal 'varchar(255)', field_1.final_db_type
94
+ assert_equal 'integer', field_2.final_type
95
+ assert_equal 'int(11)', field_2.final_db_type
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,130 @@
1
+ require 'helper'
2
+
3
+ module Coupler
4
+ module Models
5
+ class TestImport < Coupler::Test::UnitTest
6
+ def new_import(attribs = {})
7
+ values = {
8
+ :data => fixture_file_upload('people.csv'),
9
+ :project => @project
10
+ }.update(attribs)
11
+ r = Import.new(values)
12
+ r.stubs(:project_dataset).returns(stub({:all => [values[:project]]}))
13
+ r
14
+ end
15
+
16
+ def setup
17
+ super
18
+ @project = stub('project', :pk => 456, :id => 456, :associations => {})
19
+ @project.stubs(:resources_dataset).returns(stub {
20
+ stubs(:filter).returns(self)
21
+ stubs(:count).returns(0)
22
+ })
23
+ end
24
+
25
+ test "sequel model" do
26
+ assert_equal ::Sequel::Model, Models::Import.superclass
27
+ assert_equal :imports, Import.table_name
28
+ end
29
+
30
+ test "many to one project" do
31
+ assert_respond_to Models::Import.new, :project
32
+ end
33
+
34
+ test "gets name from original filename" do
35
+ import = new_import
36
+ assert_equal "People", import.name
37
+ end
38
+
39
+ test "preview with headers" do
40
+ import = new_import
41
+ preview = import.preview
42
+ assert_kind_of Array, preview
43
+ assert_equal 50, preview.length
44
+ assert_kind_of Array, preview[0]
45
+ assert_not_equal %w{id first_name last_name age}, preview[0]
46
+ end
47
+
48
+ test "discovers field names and types" do
49
+ import = new_import
50
+ expected_types = %w{integer string string integer}
51
+ expected_names = %w{id first_name last_name age}
52
+ assert_equal expected_names, import.field_names
53
+ assert_equal expected_types, import.field_types
54
+ assert_equal "id", import.primary_key_name
55
+ assert import.has_headers
56
+ end
57
+
58
+ test "discover for csv with no headers" do
59
+ tempfile = Tempfile.new('coupler-import')
60
+ tempfile.write("foo,bar,1,2,3\njunk,blah,4,5,6")
61
+ tempfile.close
62
+ import = new_import(:data => file_upload(tempfile.path))
63
+ expected_types = %w{string string integer integer integer}
64
+ assert_equal expected_types, import.field_types
65
+ assert_nil import.field_names
66
+ assert_nil import.primary_key_name
67
+ assert !import.has_headers
68
+ end
69
+
70
+ test "requires field names" do
71
+ import = new_import(:data => fixture_file_upload('no-headers.csv'))
72
+ assert_nil import.field_names
73
+ assert !import.valid?
74
+ end
75
+
76
+ test "requires primary key name" do
77
+ import = new_import(:data => fixture_file_upload('no-headers.csv'))
78
+ import.field_names = %w{id first_name last_name age}
79
+ assert !import.valid?
80
+ end
81
+
82
+ test "requires valid primary key name" do
83
+ import = new_import(:data => fixture_file_upload('no-headers.csv'))
84
+ import.field_names = %w{id first_name last_name age}
85
+ import.primary_key_name = "foo"
86
+ assert !import.valid?
87
+ end
88
+
89
+ test "requires unique field names" do
90
+ tempfile = Tempfile.new('coupler-import')
91
+ tempfile.write("id,foo,foo\n1,abc,def\n2,ghi,jkl\n3,mno,pqr")
92
+ tempfile.close
93
+
94
+ import = new_import(:data => file_upload(tempfile.path))
95
+ assert !import.valid?
96
+ end
97
+
98
+ test "requires unused resource name" do
99
+ import = new_import
100
+ @project.resources_dataset.stubs(:count).returns(1)
101
+ assert !import.valid?
102
+ end
103
+
104
+ test "dataset" do
105
+ import = new_import.save!
106
+ expected = mock('dataset')
107
+ @project.expects(:local_database).yields(mock {
108
+ expects(:[]).with(:"import_#{import.id}").returns(expected)
109
+ })
110
+ import.dataset do |actual|
111
+ assert_equal expected, actual
112
+ end
113
+ end
114
+
115
+ test "discover fields for csv with headers and varying number of fields" do
116
+ tempfile = Tempfile.new('coupler-import')
117
+ tempfile.write("id,foo,bar\n1,2,3\n1,4,5\n1,6,7,\n123,456,789,,\n")
118
+ tempfile.close
119
+
120
+ import = new_import(:data => file_upload(tempfile.path))
121
+ expected_types = %w{integer integer integer string string}
122
+ expected_names = %w{id foo bar}
123
+ assert_equal expected_names, import.field_names
124
+ assert_equal expected_types, import.field_types
125
+ assert_equal "id", import.primary_key_name
126
+ assert import.has_headers
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,115 @@
1
+ require 'helper'
2
+
3
+ module Coupler
4
+ module Models
5
+ class TestJob < Coupler::Test::UnitTest
6
+ def new_job(attribs = {})
7
+ j = Job.new(attribs)
8
+ if attribs[:resource]
9
+ j.stubs(:resource_dataset).returns(stub({:all => [attribs[:resource]]}))
10
+ end
11
+ if attribs[:scenario]
12
+ j.stubs(:scenario_dataset).returns(stub({:all => [attribs[:scenario]]}))
13
+ end
14
+ j
15
+ end
16
+
17
+ def setup
18
+ super
19
+ @resource = stub('resource', :pk => 456, :id => 456, :associations => {})
20
+ @scenario = stub('scenario', :pk => 456, :id => 456, :associations => {})
21
+ end
22
+
23
+ test "sequel model" do
24
+ assert_equal ::Sequel::Model, Job.superclass
25
+ assert_equal :jobs, Job.table_name
26
+ end
27
+
28
+ test "belongs to resource" do
29
+ assert_respond_to Job.new, :resource
30
+ end
31
+
32
+ test "belongs to scenario" do
33
+ assert_respond_to Job.new, :scenario
34
+ end
35
+
36
+ test "percent completed" do
37
+ job = new_job(:name => 'transform', :resource => @resource, :total => 200, :completed => 54)
38
+ assert_equal 27, job.percent_completed
39
+ job.total = 0
40
+ assert_equal 0, job.percent_completed
41
+ end
42
+
43
+ test "execute transform" do
44
+ job = new_job(:name => 'transform', :resource => @resource).save!
45
+
46
+ now = Time.now
47
+ @resource.expects(:source_dataset_count).returns(12345)
48
+ seq = sequence("update")
49
+ job.expects(:update).with(:status => 'running', :total => 12345, :started_at => now).in_sequence(seq)
50
+ @resource.expects(:transform!).in_sequence(seq)
51
+ job.expects(:update).with(:status => 'done', :completed_at => now).in_sequence(seq)
52
+ Timecop.freeze(now) { job.execute }
53
+ end
54
+
55
+ test "failed transform sets failed status" do
56
+ job = new_job(:name => 'transform', :resource => @resource).save!
57
+
58
+ now = Time.now
59
+ @resource.stubs(:source_dataset_count).returns(12345)
60
+ seq = sequence("update")
61
+ job.expects(:update).with(:status => 'running', :total => 12345, :started_at => now).in_sequence(seq)
62
+ fake_exception_klass = Class.new(Exception)
63
+ @resource.expects(:transform!).raises(fake_exception_klass.new).in_sequence(seq)
64
+ job.expects(:update).with(:status => 'failed', :completed_at => now).in_sequence(seq)
65
+
66
+ Timecop.freeze(now) do
67
+ begin
68
+ job.execute
69
+ rescue fake_exception_klass
70
+ end
71
+ end
72
+ end
73
+
74
+ test "execute run scenario" do
75
+ job = new_job(:name => 'run_scenario', :scenario => @scenario).save!
76
+
77
+ now = Time.now
78
+ seq = sequence("update")
79
+ job.expects(:update).with(:status => 'running', :started_at => now).in_sequence(seq)
80
+ @scenario.expects(:run!).in_sequence(seq)
81
+ job.expects(:update).with(:status => 'done', :completed_at => now).in_sequence(seq)
82
+
83
+ Timecop.freeze(now) { job.execute }
84
+ end
85
+
86
+ test "failed run scenario sets failed status" do
87
+ job = new_job(:name => 'run_scenario', :scenario => @scenario).save!
88
+
89
+ now = Time.now
90
+ seq = sequence("update")
91
+ job.expects(:update).with(:status => 'running', :started_at => now).in_sequence(seq)
92
+ fake_exception_klass = Class.new(Exception)
93
+ @scenario.expects(:run!).raises(fake_exception_klass.new).in_sequence(seq)
94
+ job.expects(:update).with(:status => 'failed', :completed_at => now).in_sequence(seq)
95
+
96
+ Timecop.freeze(now) do
97
+ begin
98
+ job.execute
99
+ rescue fake_exception_klass
100
+ end
101
+ end
102
+ end
103
+
104
+ test "recently accessed" do
105
+ now = Time.now
106
+ job_1 = job_2 = job_3 = job_4 = nil
107
+ Timecop.freeze(now - 3) { job_1 = new_job(:name => "run_scenario", :scenario => @scenario).save! }
108
+ Timecop.freeze(now - 2) { job_2 = new_job(:name => "run_scenario", :scenario => @scenario).save! }
109
+ Timecop.freeze(now - 1) { job_3 = new_job(:name => "run_scenario", :scenario => @scenario).save! }
110
+ Timecop.freeze(now ) { job_4 = new_job(:name => "run_scenario", :scenario => @scenario).save! }
111
+ assert_equal [job_4, job_3, job_2], Job.recently_accessed
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,82 @@
1
+ require 'helper'
2
+
3
+ module Coupler
4
+ module Models
5
+ class TestMatcher < Coupler::Test::UnitTest
6
+ def new_matcher(attribs = {})
7
+ values = {
8
+ :scenario => @scenario,
9
+ :comparisons_attributes => [{
10
+ :lhs_type => 'field', :raw_lhs_value => 1,
11
+ :rhs_type => 'field', :raw_rhs_value => 2,
12
+ :operator => 'equals'
13
+ }]
14
+ }.update(attribs)
15
+ m = Matcher.new(values)
16
+ m.stubs(:scenario_dataset).returns(stub({:all => [values[:scenario]]}))
17
+ m
18
+ end
19
+
20
+ def setup
21
+ super
22
+ @scenario = stub('scenario', :pk => 456, :id => 456, :associations => {})
23
+ @field_1 = stub('field 1', :pk => 1, :id => 1, :associations => {})
24
+ @field_2 = stub('field 2', :pk => 2, :id => 2, :associations => {})
25
+ end
26
+
27
+ test "sequel model" do
28
+ assert_equal ::Sequel::Model, Matcher.superclass
29
+ assert_equal :matchers, Matcher.table_name
30
+ end
31
+
32
+ test "many to one scenario" do
33
+ assert_respond_to Matcher.new, :scenario
34
+ end
35
+
36
+ test "one to many comparisons" do
37
+ assert_respond_to Matcher.new, :comparisons
38
+ end
39
+
40
+ test "nested attributes for comparisons" do
41
+ assert_respond_to Matcher.new, :comparisons_attributes=
42
+ end
43
+
44
+ #test "deletes comparisons via nested attributes" do
45
+ #matcher = new_matcher({
46
+ #:comparisons_attributes => [
47
+ #{
48
+ #'lhs_type' => 'field', 'raw_lhs_value' => fields[1].id.to_s,
49
+ #'rhs_type' => 'field', 'raw_rhs_value' => fields[2].id.to_s,
50
+ #'operator' => 'equals'
51
+ #},
52
+ #{
53
+ #'lhs_type' => 'integer', 'raw_lhs_value' => 1,
54
+ #'rhs_type' => 'integer', 'raw_rhs_value' => 1,
55
+ #'operator' => 'equals'
56
+ #}
57
+ #]
58
+ #})
59
+ #assert_equal 2, matcher.comparisons_dataset.count
60
+
61
+ #comparison = matcher.comparisons_dataset.first
62
+ #matcher.update({
63
+ #:updated_at => Time.now,
64
+ #:comparisons_attributes => [{:id => comparison.id, :_delete => true}]
65
+ #})
66
+ #assert_equal 1, matcher.comparisons_dataset.count
67
+ #end
68
+
69
+ test "requires at least one field to field comparison" do
70
+ matcher = new_matcher
71
+ matcher.expects(:comparisons).returns([stub(:rhs_type => "integer", :lhs_type => "integer")])
72
+ assert !matcher.valid?
73
+ end
74
+
75
+ test "cross_match is true when a comparison is a cross match" do
76
+ matcher = new_matcher
77
+ matcher.expects(:comparisons).returns([mock(:cross_match? => true)])
78
+ assert matcher.cross_match?
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,102 @@
1
+ require 'helper'
2
+
3
+ module Coupler
4
+ module Models
5
+ class TestProject < Coupler::Test::UnitTest
6
+ test "sequel model" do
7
+ assert_equal ::Sequel::Model, Project.superclass
8
+ assert_equal :projects, Project.table_name
9
+ end
10
+
11
+ test "one to many resources" do
12
+ assert_respond_to Project.new, :resources
13
+ end
14
+
15
+ test "one to many scenarios" do
16
+ assert_respond_to Project.new, :scenarios
17
+ end
18
+
19
+ test "requires name" do
20
+ project = Project.new
21
+ assert !project.valid?
22
+
23
+ project.name = ""
24
+ assert !project.valid?
25
+ end
26
+
27
+ test "requires unique name" do
28
+ project_1 = Project.create(:name => "foo")
29
+ project_2 = Project.new(:name => "foo")
30
+ assert !project_2.valid?
31
+ end
32
+
33
+ test "sets slug from name" do
34
+ project = Project.create('name' => 'Foo bar')
35
+ assert_equal "foo_bar", project.slug
36
+ end
37
+
38
+ test "requires unique slug" do
39
+ project_1 = Project.create(:name => "foo", :slug => 'foo')
40
+ project_2 = Project.new(:name => 'bar', :slug => 'foo')
41
+ assert !project_2.valid?
42
+
43
+ project_2.slug = "bar"
44
+ assert project_2.valid?
45
+ project_2.save
46
+
47
+ project_2.slug = "foo"
48
+ assert !project_2.valid?
49
+ end
50
+
51
+ test "saves existing project" do
52
+ project = Project.create(:name => "foo")
53
+ project.description = "Foo"
54
+ assert project.valid?
55
+ project.save
56
+ end
57
+
58
+ test "local_database" do
59
+ project = Project.create(:name => "foo")
60
+ FileUtils.rm(Dir[Base.db_path("project_#{project.id}")+".*"])
61
+
62
+ project.local_database do |db|
63
+ assert_kind_of Sequel::JDBC::Database, db
64
+ assert_match /project_#{project.id}/, db.uri
65
+ assert db.test_connection
66
+ end
67
+ end
68
+
69
+ test "deletes dependencies after destroy, but not versions" do
70
+ project = Project.create(:name => "foo")
71
+ project.expects(:resources_dataset).returns([mock(:delete_versions_on_destroy= => nil, :destroy => nil)])
72
+ project.expects(:scenarios_dataset).returns([mock(:delete_versions_on_destroy= => nil, :destroy => nil)])
73
+ project.destroy
74
+ end
75
+
76
+ test "deletes local database after destroy" do
77
+ project = Project.create(:name => "foo")
78
+ project.local_database { |db| db.test_connection } # force creation of database
79
+ project.destroy
80
+ files = Dir[Base.db_path("project_#{project.id}")+".*"]
81
+ assert files.empty?, files.inspect
82
+ end
83
+
84
+ test "deletes dependencies and versions after destroy" do
85
+ project = Project.create(:name => "foo")
86
+ project.delete_versions_on_destroy = true
87
+ project.expects(:resources_dataset).returns([mock(:destroy => nil) {
88
+ expects(:delete_versions_on_destroy=).with(true)
89
+ }])
90
+ project.expects(:scenarios_dataset).returns([mock(:destroy => nil) {
91
+ expects(:delete_versions_on_destroy=).with(true)
92
+ }])
93
+ project.destroy
94
+ assert_equal 0, Database.instance[:projects_versions].filter(:current_id => project.id).count
95
+ end
96
+
97
+ #def test_local_database_uses_connection_class
98
+ #pend
99
+ #end
100
+ end
101
+ end
102
+ end