coupler 0.0.1-java

Sign up to get free protection for your applications and to get access to all the features.
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