oracle-model-generator 0.4.1 → 0.5.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.
data/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # MAINTAINER WANTED
2
+
3
+ As of 2019 I haven't used Oracle or this library for many years. I would like
4
+ to turn it over to someone who is. If you are interested please contact me,
5
+ and we can discuss transferring the repository.
6
+
7
+ ## Description
8
+ A library for generating an ActiveRecord model from an existing Oracle table.
9
+ This will install an "omg" executable that you can use from the command line.
10
+
11
+ ## Synopsis
12
+ Using the command line tool:
13
+
14
+ `omg -d your_database -t locations -u some_user -p some_password`
15
+
16
+ The above command results in a file called "location.rb". This is an
17
+ ActiveRecord model declaration, with all validations, primary keys,
18
+ table name and belongs_to relationships defined.
19
+
20
+ If your LOCATIONS table looks like this:
21
+
22
+ ```sql
23
+ create table locations(
24
+ location_id number(4,0) primary key,
25
+ street_address varchar2(40),
26
+ postal_code varchar2(12),
27
+ city varchar2(30) not null
28
+ state_province varchar2(25),
29
+ country_id CHAR(2),
30
+ constraint "LOC_C_ID_FK" FOREIGN KEY (country_id)
31
+ references COUNTRIES (country_id)
32
+ )
33
+ ```
34
+
35
+ The omg library will generate this:
36
+
37
+ ```ruby
38
+ class Location < ActiveRecord::Base
39
+ set_table_name :locations
40
+ set_primary_key :location_id
41
+
42
+ # Table relationships
43
+
44
+ belongs_to :countries
45
+
46
+ # Validations
47
+
48
+ validates :location_id, :presence => true, :numericality => {
49
+ :less_than_or_equal_to => 9999,
50
+ :greater_than_or_equal_to => -9999,
51
+ :only_integer => true
52
+ }
53
+
54
+ validates :street_address, :length => {:maximum => 40}
55
+ validates :postal_code, :length => {:maximum => 12}
56
+ validates :city, :length => {:maximum => 30}, :presence => true
57
+ validates :state_province, :length => {:maximum => 25}
58
+ validates :country_id, :length => {:maximum => 2}
59
+ end
60
+ ```
61
+
62
+ It will also generate a corresponding test file using test-unit 2 by default.
63
+ For the above example you will see some tests like this:
64
+
65
+ ```ruby
66
+ class TC_Location < Test::Unit::TestCase
67
+ def setup
68
+ @location = Location.new
69
+ end
70
+
71
+ test 'table name is locations' do
72
+ assert_equal('locations', Location.table_name)
73
+ end
74
+
75
+ test 'primary key is location_id' do
76
+ assert_equal('location_id', Location.primary_key)
77
+ end
78
+
79
+ test 'location_id basic functionality' do
80
+ assert_respond_to(@location, :location_id)
81
+ assert_nothing_raised{ @location.location_id }
82
+ assert_kind_of(Numeric, @location.location_id)
83
+ end
84
+
85
+ test 'location_id must be a number' do
86
+ @location.location_id = 'test_string'
87
+ assert_false(@location.valid?)
88
+ assert_true(@location.errors[:location_id].include?('is not a number'))
89
+ end
90
+
91
+ test 'location_id cannot exceed the value 9999' do
92
+ @location.location_id = 10000
93
+ assert_false(@location.valid?)
94
+ assert_true(@location.errors[:location_id].include?('must be less than or equal to 9999'))
95
+ end
96
+
97
+ # ... and so on.
98
+ end
99
+ ```
100
+
101
+ ## Requirements
102
+ * ruby-oci8
103
+ * getopt
104
+
105
+ ## Running the specs
106
+ Run `docker-compose run --rm oracle-model-generator bundle exec rspec`.
107
+
108
+ You may need to use sudo. No guarantees on MacOS because of known issues
109
+ with the Oracle instant client.
110
+
111
+ ## Optional Libraries
112
+ If you want to be able to avoid specifying a username and password on the
113
+ command line then you will need the `dbi-dbrc` library.
114
+
115
+ If you want your models to support multiple primary keys, then you will
116
+ need to install the `composite_primary_keys` library.
117
+
118
+ If you want date format validations, then you will need to install the
119
+ `validates_timeliness` library.
120
+
121
+ ## What this library doesn't do
122
+ I do not attempt to set `has_many` or `has_one` relationships. There's no good
123
+ way to determine that relationship (one or many?). Besides, in practice I
124
+ find that most people set custom has_xxx relationships that go over and
125
+ above what's set in the Oracle database anyway for purposes of their
126
+ application.
127
+
128
+ I also do not go out of my way to get the model name correct with regards
129
+ to singular vs plural. I do a simple guess that covers most cases, but
130
+ complex cases will break it. It's much easier for you to rename a class or
131
+ file name than it is for me to get this 100% correct.
132
+
133
+ As of 0.3.1 there's also the `--class` option that let's you explicitly
134
+ set it if you like.
135
+
136
+ ## Author's Comments
137
+ I chose not to patch the `legacy_data` library because I have no interest in
138
+ supporting other vendors other than Oracle with this library. By focusing only
139
+ on Oracle I could take advantage of ruby-oci8 features. In addition, I have no
140
+ interest in making this a Rails plugin, and I needed the support of multiple
141
+ primary keys.
142
+
143
+ ## Future Plans (originally)
144
+ * Add support for views.
145
+ * Add automatic test suite generation for rspec.
146
+ * Explicitly set :foreign_key if using CPK in belongs_to relationships.
147
+ * The output could use a little formatting love.
148
+
149
+ ## Acknowlegements
150
+ Thanks go to Daniel Luna for his --class patch.
151
+
152
+ ## Known Issues
153
+ None known. If you find any issues, please report them on the github project
154
+ page at http://www.github.com/djberg96/oracle-model-generator.
155
+
156
+ ## Warranty
157
+ This package is provided "as is" and without any express or
158
+ implied warranties, including, without limitation, the implied
159
+ warranties of merchantability and fitness for a particular purpose.
160
+
161
+ ## Copyright
162
+ (C) 2010-2025 Daniel J. Berger
163
+ All Rights Reserved
164
+
165
+ ## License
166
+ Apache-2.0
167
+
168
+ ## Author
169
+ Daniel J. Berger
data/Rakefile CHANGED
@@ -1,16 +1,24 @@
1
1
  require 'rake'
2
- require 'rake/testtask'
3
2
  require 'rake/clean'
3
+ require 'rspec/core/rake_task'
4
4
 
5
- CLEAN.include("**/*.gem", "**/*.rbc", "**/*.log")
5
+ CLEAN.include("**/*.gem", "**/*.rbc", "**/*.log", "**/*.lock")
6
+
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.pattern = 'spec/**/*_spec.rb'
9
+ t.rspec_opts = ['--format', 'documentation', '--color']
10
+ end
11
+
12
+ desc 'Run RSpec tests'
13
+ task :rspec => :spec
6
14
 
7
15
  namespace 'gem' do
8
16
  desc 'Create the oracle-model-generator gem'
9
17
  task :create => :clean do
10
18
  require 'rubygems/package'
11
- spec = eval(IO.read('oracle-model-generator.gemspec'))
19
+ spec = Gem::Specification.load('oracle-model-generator.gemspec')
12
20
  spec.signing_key = File.join(Dir.home, '.ssh', 'gem-private_key.pem')
13
- Gem::Package.build(spec, true)
21
+ Gem::Package.build(spec)
14
22
  end
15
23
 
16
24
  desc 'Install the oracle-model-generator gem'
@@ -20,9 +28,5 @@ namespace 'gem' do
20
28
  end
21
29
  end
22
30
 
23
- Rake::TestTask.new do |t|
24
- t.warning = true
25
- t.verbose = true
26
- end
27
-
28
- task :default => :test
31
+ # Set default task to run both test suites
32
+ task :default => [:spec]
data/bin/omg CHANGED
File without changes
@@ -1,21 +1,26 @@
1
1
  -----BEGIN CERTIFICATE-----
2
- MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MREwDwYDVQQDDAhkamJl
2
+ MIIEcDCCAtigAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MREwDwYDVQQDDAhkamJl
3
3
  cmc5NjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29t
4
- MB4XDTE1MDkwMjIwNDkxOFoXDTE2MDkwMTIwNDkxOFowPzERMA8GA1UEAwwIZGpi
4
+ MB4XDTE4MDMxODE1MjIwN1oXDTI4MDMxNTE1MjIwN1owPzERMA8GA1UEAwwIZGpi
5
5
  ZXJnOTYxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
6
- bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMyTkvXqRp6hLs9eoJOS
7
- Hmi8kRYbq9Vkf15/hMxJpotYMgJVHHWrmDcC5Dye2PbnXjTkKf266Zw0PtT9h+lI
8
- S3ts9HO+vaCFSMwFFZmnWJSpQ3CNw2RcHxjWkk9yF7imEM8Kz9ojhiDXzBetdV6M
9
- gr0lV/alUr7TNVBDngbXEfTWscyXh1qd7xZ4EcOdsDktCe5G45N/o3662tPQvJsi
10
- FOF0CM/KuBsa/HL1/eoEmF4B3EKIRfTHrQ3hu20Kv3RJ88QM4ec2+0dd97uX693O
11
- zv6981fyEg+aXLkxrkViM/tz2qR2ZE0jPhHTREPYeMEgptRkTmWSKAuLVWrJEfgl
12
- DtkCAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFEwe
13
- nn6bfJADmuIDiMSOzedOrL+xMB0GA1UdEQQWMBSBEmRqYmVyZzk2QGdtYWlsLmNv
14
- bTAdBgNVHRIEFjAUgRJkamJlcmc5NkBnbWFpbC5jb20wDQYJKoZIhvcNAQEFBQAD
15
- ggEBAHmNOCWoDVD75zHFueY0viwGDVP1BNGFC+yXcb7u2GlK+nEMCORqzURbYPf7
16
- tL+/hzmePIRz7i30UM//64GI1NLv9jl7nIwjhPpXpf7/lu2I9hOTsvwSumb5UiKC
17
- /sqBxI3sfj9pr79Wpv4MuikX1XPik7Ncb7NPsJPw06Lvyc3Hkg5X2XpPtLtS+Gr2
18
- wKJnmzb5rIPS1cmsqv0M9LPWflzfwoZ/SpnmhagP+g05p8bRNKjZSA2iImM/GyYZ
19
- EJYzxdPOrx2n6NYR3Hk+vHP0U7UBSveI6+qx+ndQYaeyCn+GRX2PKS9h66YF/Q1V
20
- tGSHgAmcLlkdGgan182qsE/4kKM=
6
+ bTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALgfaroVM6CI06cxr0/h
7
+ A+j+pc8fgpRgBVmHFaFunq28GPC3IvW7Nvc3Y8SnAW7pP1EQIbhlwRIaQzJ93/yj
8
+ u95KpkP7tA9erypnV7dpzBkzNlX14ACaFD/6pHoXoe2ltBxk3CCyyzx70mTqJpph
9
+ 75IB03ni9a8yqn8pmse+s83bFJOAqddSj009sGPcQO+QOWiNxqYv1n5EHcvj2ebO
10
+ 6hN7YTmhx7aSia4qL/quc4DlIaGMWoAhvML7u1fmo53CYxkKskfN8MOecq2vfEmL
11
+ iLu+SsVVEAufMDDFMXMJlvDsviolUSGMSNRTujkyCcJoXKYYxZSNtIiyd9etI0X3
12
+ ctu0uhrFyrMZXCedutvXNjUolD5r9KGBFSWH1R9u2I3n3SAyFF2yzv/7idQHLJJq
13
+ 74BMnx0FIq6fCpu5slAipvxZ3ZkZpEXZFr3cIBtO1gFvQWW7E/Y3ijliWJS1GQFq
14
+ 058qERadHGu1yu1dojmFRo6W2KZvY9al2yIlbkpDrD5MYQIDAQABo3cwdTAJBgNV
15
+ HRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUFZsMapgzJimzsbaBG2Tm8j5e
16
+ AzgwHQYDVR0RBBYwFIESZGpiZXJnOTZAZ21haWwuY29tMB0GA1UdEgQWMBSBEmRq
17
+ YmVyZzk2QGdtYWlsLmNvbTANBgkqhkiG9w0BAQsFAAOCAYEAW2tnYixXQtKxgGXq
18
+ /3iSWG2bLwvxS4go3srO+aRXZHrFUMlJ5W0mCxl03aazxxKTsVVpZD8QZxvK91OQ
19
+ h9zr9JBYqCLcCVbr8SkmYCi/laxIZxsNE5YI8cC8vvlLI7AMgSfPSnn/Epq1GjGY
20
+ 6L1iRcEDtanGCIvjqlCXO9+BmsnCfEVehqZkQHeYczA03tpOWb6pon2wzvMKSsKH
21
+ ks0ApVdstSLz1kzzAqem/uHdG9FyXdbTAwH1G4ZPv69sQAFAOCgAqYmdnzedsQtE
22
+ 1LQfaQrx0twO+CZJPcRLEESjq8ScQxWRRkfuh2VeR7cEU7L7KqT10mtUwrvw7APf
23
+ DYoeCY9KyjIBjQXfbj2ke5u1hZj94Fsq9FfbEQg8ygCgwThnmkTrrKEiMSs3alYR
24
+ ORVCZpRuCPpmC8qmqxUnARDArzucjaclkxjLWvCVHeFa9UP7K3Nl9oTjJNv+7/jM
25
+ WZs4eecIcUc4tKdHxcAJ0MO/Dkqq7hGaiHpwKY76wQ1+8xAh
21
26
  -----END CERTIFICATE-----
@@ -0,0 +1,41 @@
1
+ services:
2
+ # Oracle Express Edition database for testing
3
+ oracle-db:
4
+ image: gvenzl/oracle-xe:21-slim
5
+ environment:
6
+ - ORACLE_PASSWORD=oracle
7
+ - APP_USER=hr
8
+ - APP_USER_PASSWORD=hr
9
+ ports:
10
+ - "1521:1521"
11
+ volumes:
12
+ - oracle_data:/opt/oracle/oradata
13
+ shm_size: 1g
14
+ healthcheck:
15
+ test: ["CMD", "healthcheck.sh"]
16
+ interval: 30s
17
+ timeout: 10s
18
+ retries: 5
19
+ start_period: 60s
20
+
21
+ # Oracle Model Generator testing environment
22
+ oracle-model-generator:
23
+ build: .
24
+ depends_on:
25
+ oracle-db:
26
+ condition: service_healthy
27
+ environment:
28
+ - ORACLE_HOST=oracle-db
29
+ - ORACLE_PORT=1521
30
+ - ORACLE_SID=freepdb1
31
+ - ORACLE_USER=hr
32
+ - ORACLE_PASSWORD=hr
33
+ volumes:
34
+ - .:/app
35
+ working_dir: /app
36
+ stdin_open: true
37
+ tty: true
38
+ command: ["rake"] # This will run tests with automatic setup
39
+
40
+ volumes:
41
+ oracle_data:
@@ -4,7 +4,7 @@ module Oracle
4
4
  module Model
5
5
  class Generator
6
6
  # The version of the oracle-model-generator library
7
- VERSION = '0.4.1'
7
+ VERSION = '0.5.0'
8
8
 
9
9
  # The raw OCI8 connection.
10
10
  attr_reader :connection
@@ -1,23 +1,22 @@
1
1
  require 'rubygems'
2
2
 
3
3
  Gem::Specification.new do |spec|
4
- spec.name = 'oracle-model-generator'
5
- spec.version = '0.4.1'
6
- spec.author = 'Daniel J. Berger'
7
- spec.license = 'Artistic 2.0'
8
- spec.email = 'djberg96@gmail.com'
9
- spec.homepage = 'http://www.github.com/djberg96/oracle-model-generator'
10
- spec.summary = 'A Ruby interface for determining protocol information'
11
- spec.test_file = 'test/test_oracle_model_generator.rb'
12
- spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
13
- spec.cert_chain = spec.cert_chain = ['certs/djberg96_pub.pem']
4
+ spec.name = 'oracle-model-generator'
5
+ spec.version = '0.5.0'
6
+ spec.author = 'Daniel J. Berger'
7
+ spec.license = 'Apache-2.0'
8
+ spec.email = 'djberg96@gmail.com'
9
+ spec.homepage = 'http://www.github.com/djberg96/oracle-model-generator'
10
+ spec.summary = 'A Ruby interface for determining protocol information'
11
+ spec.test_files = Dir['spec/**/*.rb']
12
+ spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
13
+ spec.cert_chain = spec.cert_chain = ['certs/djberg96_pub.pem']
14
14
 
15
15
  spec.executables = "omg"
16
- spec.extra_rdoc_files = %w[CHANGES README MANIFEST]
17
16
 
18
- spec.add_dependency('ruby-oci8')
19
- spec.add_dependency('getopt')
20
- spec.add_development_dependency('test-unit')
17
+ spec.add_dependency('ruby-oci8', '~> 2.2')
18
+ spec.add_dependency('getopt', '~> 1.6')
19
+ spec.add_development_dependency('rspec', '~> 3.12')
21
20
 
22
21
  spec.description = <<-EOF
23
22
  The oracle-model-generator library allows you to generate an ActiveRecord
@@ -0,0 +1,176 @@
1
+ require 'spec_helper'
2
+ require 'support/oracle_connection'
3
+ require 'oracle/model/generator'
4
+
5
+ RSpec.describe Oracle::Model::Generator do
6
+ include_context 'Oracle connection'
7
+
8
+ let(:generator) { described_class.new(connection) }
9
+
10
+ describe 'class information' do
11
+ it 'has the correct version number' do
12
+ expect(Oracle::Model::Generator::VERSION).to eq('0.5.0')
13
+ end
14
+ end
15
+
16
+ describe '#initialize' do
17
+ it 'accepts an OCI8 connection object' do
18
+ expect { described_class.new(connection) }.not_to raise_error
19
+ end
20
+
21
+ it 'sets up instance variables correctly' do
22
+ expect(generator.connection).to eq(connection)
23
+ expect(generator.constraints).to eq([])
24
+ expect(generator.primary_keys).to eq([])
25
+ expect(generator.foreign_keys).to eq([])
26
+ expect(generator.dependencies).to eq([])
27
+ expect(generator.belongs_to).to eq([])
28
+ expect(generator.column_info).to eq([])
29
+ expect(generator.table).to be_nil
30
+ expect(generator.model).to be_nil
31
+ end
32
+ end
33
+
34
+ describe '#generate' do
35
+ it 'responds to the generate method' do
36
+ expect(generator).to respond_to(:generate)
37
+ end
38
+
39
+ it 'works with a table name' do
40
+ expect { generator.generate('employees') }.not_to raise_error
41
+ end
42
+
43
+ it 'works with a view name' do
44
+ expect { generator.generate('emp_details_view', true) }.not_to raise_error
45
+ end
46
+
47
+ context 'when generating from a table' do
48
+ before { generator.generate('employees') }
49
+
50
+ it 'sets the table name in uppercase' do
51
+ expect(generator.table).to eq('EMPLOYEES')
52
+ end
53
+
54
+ it 'generates a model name from the table name' do
55
+ expect(generator.model).to eq('Employee')
56
+ end
57
+
58
+ it 'sets view to false' do
59
+ expect(generator.view).to be false
60
+ end
61
+
62
+ it 'populates column information' do
63
+ expect(generator.column_info).to be_an(Array)
64
+ expect(generator.column_info).not_to be_empty
65
+ end
66
+
67
+ it 'finds primary keys' do
68
+ expect(generator.primary_keys).to be_an(Array)
69
+ expect(generator.primary_keys).to include('employee_id')
70
+ end
71
+
72
+ it 'populates constraints information' do
73
+ expect(generator.constraints).to be_an(Array)
74
+ expect(generator.constraints).not_to be_empty
75
+ end
76
+ end
77
+
78
+ context 'when generating from a view' do
79
+ before { generator.generate('emp_details_view', true) }
80
+
81
+ it 'sets the table name in uppercase' do
82
+ expect(generator.table).to eq('EMP_DETAILS_VIEW')
83
+ end
84
+
85
+ it 'generates the correct model name' do
86
+ expect(generator.model).to eq('EmpDetailsView')
87
+ end
88
+
89
+ it 'sets view to true' do
90
+ expect(generator.view).to be true
91
+ end
92
+ end
93
+ end
94
+
95
+ describe '#model' do
96
+ it 'returns the active record model name' do
97
+ generator.generate('emp_details_view', true)
98
+ expect(generator).to respond_to(:model)
99
+ expect(generator.model).to eq('EmpDetailsView')
100
+ end
101
+
102
+ it 'removes trailing s from model names' do
103
+ generator.generate('employees')
104
+ expect(generator.model).to eq('Employee')
105
+ end
106
+ end
107
+
108
+ describe '#table' do
109
+ it 'returns the uppercased table name' do
110
+ generator.generate('emp_details_view', true)
111
+ expect(generator).to respond_to(:table)
112
+ expect(generator.table).to eq('EMP_DETAILS_VIEW')
113
+ end
114
+ end
115
+
116
+ describe '#primary_keys' do
117
+ it 'returns an array of primary keys' do
118
+ generator.generate('employees')
119
+ expect(generator.primary_keys).to be_an(Array)
120
+ expect(generator.primary_keys).to include('employee_id')
121
+ end
122
+ end
123
+
124
+ describe '#column_info' do
125
+ it 'returns an array of column metadata' do
126
+ generator.generate('employees')
127
+ expect(generator.column_info).to be_an(Array)
128
+ expect(generator.column_info.length).to be > 0
129
+ end
130
+ end
131
+
132
+ describe '#constraints' do
133
+ it 'returns an array of constraint information' do
134
+ generator.generate('employees')
135
+ expect(generator.constraints).to be_an(Array)
136
+ expect(generator.constraints).not_to be_empty
137
+ end
138
+ end
139
+
140
+ describe '#foreign_keys' do
141
+ it 'returns an array of foreign key names' do
142
+ generator.generate('employees')
143
+ expect(generator.foreign_keys).to be_an(Array)
144
+ end
145
+ end
146
+
147
+ describe '#belongs_to' do
148
+ it 'returns an array of parent tables' do
149
+ generator.generate('employees')
150
+ expect(generator.belongs_to).to be_an(Array)
151
+ end
152
+ end
153
+
154
+ describe '#dependencies' do
155
+ it 'returns an array of dependent objects' do
156
+ generator.generate('employees', true)
157
+ expect(generator).to respond_to(:dependencies)
158
+ expect(generator.dependencies).to be_an(Array)
159
+
160
+ # Only check for Hash if there are dependencies
161
+ if generator.dependencies.any?
162
+ expect(generator.dependencies.first).to be_a(Hash)
163
+ end
164
+ end
165
+ end
166
+
167
+ describe '#view' do
168
+ it 'correctly identifies tables vs views' do
169
+ generator.generate('employees')
170
+ expect(generator.view).to be false
171
+
172
+ generator.generate('emp_details_view', true)
173
+ expect(generator.view).to be true
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,30 @@
1
+ # Load the library we're testing
2
+ require_relative '../lib/oracle-model-generator'
3
+
4
+ # RSpec configuration for Oracle Model Generator
5
+ RSpec.configure do |config|
6
+ # Use the expect() syntax rather than should syntax
7
+ config.expect_with :rspec do |expectations|
8
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
9
+ end
10
+
11
+ # Use the new mock framework syntax
12
+ config.mock_with :rspec do |mocks|
13
+ mocks.verify_partial_doubles = true
14
+ end
15
+
16
+ # Enable color output
17
+ config.color = true
18
+
19
+ # Use documentation format for output
20
+ config.formatter = :documentation
21
+
22
+ # Run specs in random order to surface order dependencies
23
+ config.order = :random
24
+
25
+ # Allow filtering tests by tags
26
+ config.filter_run_when_matching :focus
27
+
28
+ # Shared context for setting up test data
29
+ config.shared_context_metadata_behavior = :apply_to_host_groups
30
+ end
@@ -0,0 +1,126 @@
1
+ require 'oci8'
2
+
3
+ # Shared context for Oracle database connection and test data
4
+ RSpec.shared_context 'Oracle connection' do
5
+ before(:context) do
6
+ @oracle_connection = begin
7
+ # In Docker environment, use the service name instead of localhost
8
+ host = ENV['ORACLE_HOST'] || 'localhost'
9
+ port = ENV['ORACLE_PORT'] || '1521'
10
+ sid = ENV['ORACLE_SID'] || 'freepdb1'
11
+ database_str = "#{host}:#{port}/#{sid}"
12
+
13
+ OCI8.new(
14
+ ENV['ORACLE_USER'] || 'hr',
15
+ ENV['ORACLE_PASSWORD'] || 'oracle',
16
+ database_str
17
+ )
18
+ rescue OCIError => e
19
+ warn "Database connection failed: #{e.message}"
20
+ warn "Make sure Oracle database is running and accessible"
21
+ warn "Connection string: #{ENV['ORACLE_HOST'] || 'localhost'}:#{ENV['ORACLE_PORT'] || '1521'}/#{ENV['ORACLE_SID'] || 'freepdb1'}"
22
+ warn "User: #{ENV['ORACLE_USER'] || 'hr'}"
23
+ raise e
24
+ end
25
+ setup_test_data
26
+ end
27
+
28
+ after(:context) do
29
+ # Cleanup test tables and views
30
+ cleanup_test_data
31
+
32
+ # Close the connection
33
+ @oracle_connection&.logoff
34
+ end
35
+
36
+ # Use a method instead of let for context-level variables
37
+ def connection
38
+ @oracle_connection
39
+ end
40
+
41
+ private
42
+
43
+ def setup_test_data
44
+ # Create test tables and views if they don't exist
45
+ setup_sample_data
46
+ end
47
+
48
+ def cleanup_test_data
49
+ # Drop test views first (to avoid dependency issues)
50
+ begin
51
+ @oracle_connection.exec('DROP VIEW hr.employees_by_department') rescue nil
52
+ rescue => e
53
+ # Ignore errors if views don't exist
54
+ end
55
+
56
+ # Note: We don't drop HR schema tables as they might be used by other tests
57
+ end
58
+
59
+ def setup_sample_data
60
+ # Check if employees table exists and has data
61
+ begin
62
+ cursor = @oracle_connection.parse('SELECT COUNT(*) FROM employees')
63
+ cursor.exec
64
+ count = cursor.fetch[0]
65
+ cursor.close
66
+
67
+ if count == 0
68
+ # Insert sample data into employees table
69
+ @oracle_connection.exec(<<~SQL)
70
+ INSERT INTO employees (employee_id, first_name, last_name, email, phone_number,
71
+ hire_date, job_id, salary, commission_pct, manager_id, department_id)
72
+ VALUES (1, 'John', 'Doe', 'john.doe@example.com', '555-1234',
73
+ DATE '2020-01-15', 'IT_PROG', 5000, NULL, NULL, 60)
74
+ SQL
75
+
76
+ @oracle_connection.exec(<<~SQL)
77
+ INSERT INTO employees (employee_id, first_name, last_name, email, phone_number,
78
+ hire_date, job_id, salary, commission_pct, manager_id, department_id)
79
+ VALUES (2, 'Jane', 'Smith', 'jane.smith@example.com', '555-5678',
80
+ DATE '2021-03-10', 'IT_PROG', 5500, NULL, 1, 60)
81
+ SQL
82
+
83
+ @oracle_connection.commit
84
+ end
85
+
86
+ # Check if departments table exists and has data
87
+ cursor = @oracle_connection.parse('SELECT COUNT(*) FROM departments')
88
+ cursor.exec
89
+ dept_count = cursor.fetch[0]
90
+ cursor.close
91
+
92
+ if dept_count == 0
93
+ # Insert sample data into departments table
94
+ @oracle_connection.exec(<<~SQL)
95
+ INSERT INTO departments (department_id, department_name, manager_id, location_id)
96
+ VALUES (60, 'IT', 1, 1700)
97
+ SQL
98
+
99
+ @oracle_connection.exec(<<~SQL)
100
+ INSERT INTO departments (department_id, department_name, manager_id, location_id)
101
+ VALUES (50, 'Shipping', NULL, 1500)
102
+ SQL
103
+
104
+ @oracle_connection.commit
105
+ end
106
+
107
+ # Create a test view if it doesn't exist
108
+ begin
109
+ @oracle_connection.exec(<<~SQL)
110
+ CREATE OR REPLACE VIEW hr.employees_by_department AS
111
+ SELECT e.employee_id, e.first_name, e.last_name, e.email,
112
+ d.department_name, d.department_id
113
+ FROM employees e
114
+ JOIN departments d ON e.department_id = d.department_id
115
+ SQL
116
+ rescue => e
117
+ # View creation might fail if tables don't exist yet, that's okay
118
+ warn "Could not create test view: #{e.message}"
119
+ end
120
+
121
+ rescue => e
122
+ warn "Could not set up sample data: #{e.message}"
123
+ # Continue with tests even if sample data setup fails
124
+ end
125
+ end
126
+ end
data.tar.gz.sig CHANGED
Binary file