my_obfuscate 0.3.0 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,91 @@
1
+ class MyObfuscate
2
+ class Mysql
3
+ INSERT_REGEX = /^\s*INSERT INTO `(.*?)` \((.*?)\) VALUES\s*/i
4
+
5
+ def parse_insert_statement(line)
6
+ if regex_match = INSERT_REGEX.match(line)
7
+ {
8
+ :table_name => regex_match[1].to_sym,
9
+ :column_names => regex_match[2].split(/`\s*,\s*`/).map { |col| col.gsub('`', "").to_sym }
10
+ }
11
+ end
12
+ end
13
+
14
+ def rows_to_be_inserted(line)
15
+ line = line.gsub(INSERT_REGEX, '').gsub(/\s*;\s*$/, '')
16
+ context_aware_mysql_string_split(line)
17
+ end
18
+
19
+ def make_valid_value_string(value)
20
+ if value.nil?
21
+ "NULL"
22
+ elsif value =~ /^0x[0-9a-fA-F]+$/
23
+ value
24
+ else
25
+ "'" + value + "'"
26
+ end
27
+ end
28
+
29
+ def make_insert_statement(table_name, column_names, values_strings)
30
+ "INSERT INTO `#{table_name}` (`#{column_names.join('`, `')}`) VALUES #{values_strings};"
31
+ end
32
+
33
+ private
34
+
35
+ # Be aware, strings must be quoted in single quotes!
36
+ def context_aware_mysql_string_split(string)
37
+ in_sub_insert = false
38
+ in_quoted_string = false
39
+ escaped = false
40
+ current_field = nil
41
+ length = string.length
42
+ fields = []
43
+ output = []
44
+
45
+ string.each_char do |i|
46
+ if escaped
47
+ escaped = false
48
+ current_field ||= ""
49
+ current_field << i
50
+ else
51
+ if i == "\\"
52
+ escaped = true
53
+ current_field ||= ""
54
+ current_field << i
55
+ elsif i == "(" && !in_quoted_string && !in_sub_insert
56
+ in_sub_insert = true
57
+ elsif i == ")" && !in_quoted_string && in_sub_insert
58
+ fields << current_field unless current_field.nil?
59
+ output << fields unless fields.length == 0
60
+ in_sub_insert = false
61
+ fields = []
62
+ current_field = nil
63
+ elsif i == "'" && !in_quoted_string
64
+ fields << current_field unless current_field.nil?
65
+ current_field = ''
66
+ in_quoted_string = true
67
+ elsif i == "'" && in_quoted_string
68
+ fields << current_field unless current_field.nil?
69
+ current_field = nil
70
+ in_quoted_string = false
71
+ elsif i == "," && !in_quoted_string && in_sub_insert
72
+ fields << current_field unless current_field.nil?
73
+ current_field = nil
74
+ elsif i == "L" && !in_quoted_string && in_sub_insert && current_field == "NUL"
75
+ current_field = nil
76
+ fields << current_field
77
+ elsif (i == " " || i == "\t") && !in_quoted_string
78
+ # Don't add whitespace not in a string
79
+ elsif in_sub_insert
80
+ current_field ||= ""
81
+ current_field << i
82
+ end
83
+ end
84
+ end
85
+
86
+ fields << current_field unless current_field.nil?
87
+ output << fields unless fields.length == 0
88
+ output
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,81 @@
1
+ class MyObfuscate
2
+ class SqlServer
3
+ INSERT_REGEX = /^\s*INSERT (?:INTO )?\[dbo\]\.\[(.*?)\] \((.*?)\) VALUES\s*/i
4
+
5
+ def parse_insert_statement(line)
6
+ if regex_match = INSERT_REGEX.match(line)
7
+ {
8
+ :table_name => regex_match[1].to_sym,
9
+ :column_names => regex_match[2].split(/\]\s*,\s*\[/).map { |col| col.gsub(/[\[\]]/, "").to_sym }
10
+ }
11
+ end
12
+ end
13
+
14
+ def rows_to_be_inserted(line)
15
+ line = line.gsub(INSERT_REGEX, '').gsub(/\s*;?\s*$/, '').gsub(/^\(/, '').gsub(/\)$/, '')
16
+ context_aware_sql_server_string_split(line)
17
+ end
18
+
19
+ def make_valid_value_string(value)
20
+ if value.nil?
21
+ "NULL"
22
+ elsif value.match(/^[A-Z]+\(.*?\)$/)
23
+ value
24
+ else
25
+ "N'#{value}'"
26
+ end
27
+ end
28
+
29
+ def make_insert_statement(table_name, column_names, values_strings)
30
+ "INSERT [dbo].[#{table_name}] ([#{column_names.join("], [")}]) VALUES #{values_strings};"
31
+ end
32
+
33
+ private
34
+
35
+ def context_aware_sql_server_string_split(string)
36
+ in_quoted_string = false
37
+ backslash_escape = false
38
+ previous_char_single_quote = false
39
+ current_field_value = nil
40
+ completed_fields = []
41
+
42
+ string.each_char do |char|
43
+ if char == "'" && !in_quoted_string
44
+ if current_field_value != "N"
45
+ completed_fields << current_field_value unless current_field_value.nil?
46
+ end
47
+ current_field_value = ""
48
+ in_quoted_string = true
49
+ elsif previous_char_single_quote
50
+ previous_char_single_quote = false
51
+ if char == "'"
52
+ current_field_value << "''"
53
+ else
54
+ completed_fields << current_field_value unless current_field_value.nil?
55
+ in_quoted_string = false
56
+ current_field_value = nil
57
+ end
58
+ elsif char == "'" && in_quoted_string
59
+ previous_char_single_quote = true
60
+ elsif char == "," && !in_quoted_string
61
+ completed_fields << current_field_value unless current_field_value.nil?
62
+ current_field_value = nil
63
+ elsif char == "L" && !in_quoted_string && current_field_value == "NUL"
64
+ current_field_value = nil
65
+ completed_fields << current_field_value
66
+ elsif (char == " " || char == "\t") && !in_quoted_string
67
+ if !current_field_value.nil? && current_field_value.start_with?("CAST(")
68
+ current_field_value << char
69
+ end
70
+ # Don't add whitespace not in a string
71
+ else
72
+ current_field_value ||= ""
73
+ current_field_value << char
74
+ end
75
+ end
76
+
77
+ completed_fields << current_field_value unless current_field_value.nil?
78
+ [completed_fields]
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,3 @@
1
+ class MyObfuscate
2
+ VERSION = "0.3.7"
3
+ end
data/my_obfuscate.gemspec CHANGED
@@ -1,54 +1,22 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
1
  # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "my_obfuscate/version"
5
4
 
6
5
  Gem::Specification.new do |s|
7
6
  s.name = %q{my_obfuscate}
8
- s.version = "0.3.0"
7
+ s.version = MyObfuscate::VERSION
9
8
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Andrew Cantino", "Dave Willett", "Mike Grafton", "Mason Glaves"]
12
- s.date = %q{2009-10-09}
9
+ s.authors = ["Andrew Cantino", "Dave Willett", "Mike Grafton", "Mason Glaves", "Greg Bell", "Mavenlink"]
13
10
  s.description = %q{Standalone Ruby code for the selective rewriting of MySQL dumps in order to protect user privacy.}
14
- s.email = %q{andrew@pivotallabs.com}
15
- s.extra_rdoc_files = [
16
- "LICENSE",
17
- "README.rdoc"
18
- ]
19
- s.files = [
20
- ".document",
21
- ".gitignore",
22
- "LICENSE",
23
- "README.rdoc",
24
- "Rakefile",
25
- "VERSION",
26
- "lib/my_obfuscate.rb",
27
- "my_obfuscate.gemspec",
28
- "spec/my_obfuscate_spec.rb",
29
- "spec/spec_helper.rb"
30
- ]
31
- s.homepage = %q{http://github.com/honkster/myobfuscate}
32
- s.rdoc_options = ["--charset=UTF-8"]
33
- s.require_paths = ["lib"]
34
- s.rubyforge_project = %q{my-obfuscate}
35
- s.rubygems_version = %q{1.3.5}
11
+ s.email = %q{andrew@iterationlabs.com}
12
+ s.homepage = %q{http://github.com/iterationlabs/my_obfuscate}
36
13
  s.summary = %q{Standalone Ruby code for the selective rewriting of MySQL dumps in order to protect user privacy.}
37
- s.test_files = [
38
- "spec/my_obfuscate_spec.rb",
39
- "spec/spec_helper.rb"
40
- ]
41
14
 
42
- if s.respond_to? :specification_version then
43
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
- s.specification_version = 3
15
+ s.add_development_dependency "rspec"
16
+ s.add_dependency "faker", ">= 1.0.1"
45
17
 
46
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
47
- s.add_development_dependency(%q<rspec>, [">= 0"])
48
- else
49
- s.add_dependency(%q<rspec>, [">= 0"])
50
- end
51
- else
52
- s.add_dependency(%q<rspec>, [">= 0"])
53
- end
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
54
22
  end
@@ -1,51 +1,19 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe MyObfuscate do
4
- describe "MyObfuscate.context_aware_mysql_string_split" do
5
- it "should split a mysql string into fields" do
6
- string = "('bob@bob.com','bob', 'somethingelse1', 25, '2', 10, 'hi')"
7
- fields = [['bob@bob.com', 'bob', 'somethingelse1', '25', '2', '10', "hi"]]
8
- MyObfuscate.context_aware_mysql_string_split(string).should == fields
9
- end
10
-
11
- it "should work ok with escaped characters" do
12
- string = "('bob,@bob.c , om', 'bo\\', b', 'some\"thin\\gel\\\\\\'se1', 25, '2', 10, 'hi', 5)"
13
- fields = [['bob,@bob.c , om', 'bo\\\', b', 'some"thin\\gel\\\\\\\'se1', '25', '2', '10', "hi", "5"]]
14
- MyObfuscate.context_aware_mysql_string_split(string).should == fields
15
- end
16
-
17
- it "should work with multiple subinserts" do
18
- string = "(1,2,3, '((m))(oo()s,e'), ('bob,@bob.c , om', 'bo\\', b', 'some\"thin\\gel\\\\\\'se1', 25, '2', 10, 'hi', 5)"
19
- fields = [["1", "2", "3", "((m))(oo()s,e"], ['bob,@bob.c , om', 'bo\\\', b', 'some"thin\\gel\\\\\\\'se1', '25', '2', '10', "hi", "5"]]
20
- MyObfuscate.context_aware_mysql_string_split(string).should == fields
21
- end
22
-
23
- it "should work ok with NULL values" do
24
- string = "(NULL , 'bob@bob.com','bob', NULL, 25, '2', NULL, 'hi', NULL )"
25
- fields = [[nil, 'bob@bob.com', 'bob', nil, '25', '2', nil, "hi", nil]]
26
- MyObfuscate.context_aware_mysql_string_split(string).should == fields
27
- end
28
-
29
- it "should work with empty strings" do
30
- string = "(NULL , '', '' , '', 25, '2','', 'hi','')"
31
- fields = [[nil, '', '', '', '25', '2', '', "hi", '']]
32
- MyObfuscate.context_aware_mysql_string_split(string).should == fields
33
- end
34
- end
35
-
36
- describe "MyObfuscate.reasembling_each_insert" do
4
+ describe "MyObfuscate.reassembling_each_insert" do
37
5
  before do
38
6
  @column_names = [:a, :b, :c, :d]
39
7
  @test_insert = "INSERT INTO `some_table` (`a`, `b`, `c`, `d`) VALUES ('(\\'bob@bob.com','b()ob','some(thingelse1','25)('),('joe@joe.com','joe','somethingelse2','54');"
40
8
  @test_insert_passes = [
41
- ["(\\'bob@bob.com","b()ob", "some(thingelse1", "25)("],
42
- ["joe@joe.com","joe", "somethingelse2", "54"]
9
+ ["(\\'bob@bob.com", "b()ob", "some(thingelse1", "25)("],
10
+ ["joe@joe.com", "joe", "somethingelse2", "54"]
43
11
  ]
44
12
  end
45
13
 
46
14
  it "should yield each subinsert and reassemble the result" do
47
15
  count = 0
48
- reassembled = MyObfuscate.reasembling_each_insert(@test_insert, "some_table", @column_names) do |sub_insert|
16
+ reassembled = MyObfuscate.new.reassembling_each_insert(@test_insert, "some_table", @column_names) do |sub_insert|
49
17
  sub_insert.should == @test_insert_passes.shift
50
18
  count += 1
51
19
  sub_insert
@@ -57,13 +25,13 @@ describe MyObfuscate do
57
25
 
58
26
  describe "MyObfuscate.apply_table_config" do
59
27
  it "should work on email addresses" do
60
- new_row = MyObfuscate.apply_table_config(["blah", "something_else"], { :a => { :type => :email }}, [:a, :b])
28
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else"], {:a => {:type => :email}}, [:a, :b])
61
29
  new_row.length.should == 2
62
- new_row.first.should =~ /^\w+\@\w+\.\w+$/
30
+ new_row.first.should =~ /^[\w\.]+\@\w+\.\w+$/
63
31
  end
64
32
 
65
33
  it "should work on strings" do
66
- new_row = MyObfuscate.apply_table_config(["blah", "something_else", "something crazy"], { :b => { :type => :string, :length => 7 }}, [:a, :b, :c])
34
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "something crazy"], {:b => {:type => :string, :length => 7}}, [:a, :b, :c])
67
35
  new_row.length.should == 3
68
36
  new_row[1].length.should == 7
69
37
  new_row[1].should_not == "something_else"
@@ -71,67 +39,95 @@ describe MyObfuscate do
71
39
 
72
40
  describe "conditional directives" do
73
41
  it "should honor :unless conditionals" do
74
- new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], { :a=> { :type => :fixed, :string => "123", :unless => lambda {|row| row[:a] == "blah"} }}, [:a, :b, :c])
42
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :unless => lambda { |row| row[:a] == "blah" }}}, [:a, :b, :c])
75
43
  new_row[0].should_not == "123"
76
44
  new_row[0].should == "blah"
77
45
 
78
- new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], { :a=> { :type => :fixed, :string => "123", :unless => lambda {|row| row[:a] == "not blah"} }}, [:a, :b, :c])
46
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :unless => lambda { |row| row[:a] == "not blah" }}}, [:a, :b, :c])
79
47
  new_row[0].should == "123"
48
+
49
+ new_row = MyObfuscate.apply_table_config([nil, "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :unless => :nil}, :b=> {:type => :fixed, :string => "123", :unless => :nil}}, [:a, :b, :c])
50
+ new_row[0].should == nil
51
+ new_row[1].should == "123"
52
+
53
+ new_row = MyObfuscate.apply_table_config(['', "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :unless => :blank}, :b=> {:type => :fixed, :string => "123", :unless => :blank}}, [:a, :b, :c])
54
+ new_row[0].should == ''
55
+ new_row[1].should == "123"
80
56
  end
81
57
 
82
58
  it "should honor :if conditionals" do
83
- new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], { :a=> { :type => :fixed, :string => "123", :if => lambda {|row| row[:a] == "blah"} }}, [:a, :b, :c])
59
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => lambda { |row| row[:a] == "blah" }}}, [:a, :b, :c])
84
60
  new_row[0].should == "123"
85
61
 
86
- new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], { :a=> { :type => :fixed, :string => "123", :if=> lambda {|row| row[:a] == "not blah"} }}, [:a, :b, :c])
62
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if=> lambda { |row| row[:a] == "not blah" }}}, [:a, :b, :c])
87
63
  new_row[0].should_not == "123"
88
64
  new_row[0].should == "blah"
65
+
66
+ new_row = MyObfuscate.apply_table_config([nil, "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => :nil}, :b=> {:type => :fixed, :string => "123", :if => :nil}}, [:a, :b, :c])
67
+ new_row[0].should == "123"
68
+ new_row[1].should == "something_else"
69
+
70
+ new_row = MyObfuscate.apply_table_config(['', "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => :blank}, :b=> {:type => :fixed, :string => "123", :if => :blank}}, [:a, :b, :c])
71
+ new_row[0].should == "123"
72
+ new_row[1].should == "something_else"
89
73
  end
90
74
 
91
75
  it "should supply the original row values to the conditional" do
92
- new_row = MyObfuscate.apply_table_config(["blah", "something_else"], { :a => { :type => :fixed, :string => "123" }, :b => { :type => :fixed, :string => "yup", :if => lambda {|row| row[:a] == "blah"}}}, [:a, :b])
76
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else"], {:a => {:type => :fixed, :string => "123"}, :b => {:type => :fixed, :string => "yup", :if => lambda { |row| row[:a] == "blah" }}}, [:a, :b])
93
77
  new_row[0].should == "123"
94
78
  new_row[1].should == "yup"
95
79
  end
96
80
 
97
81
  it "should honor combined :unless and :if conditionals" do
98
82
  #both true
99
- new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], { :a=> { :type => :fixed, :string => "123", :if => lambda {|row| row[:a] == "blah"}, :unless => lambda {|row| row[:b] == "something_else"} }}, [:a, :b, :c])
83
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => lambda { |row| row[:a] == "blah" }, :unless => lambda { |row| row[:b] == "something_else" }}}, [:a, :b, :c])
100
84
  new_row[0].should == "blah"
101
85
 
102
86
  #both false
103
- new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], { :a=> { :type => :fixed, :string => "123", :if => lambda {|row| row[:a] == "not blah"}, :unless => lambda {|row| row[:b] == "not something_else"} }}, [:a, :b, :c])
87
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => lambda { |row| row[:a] == "not blah" }, :unless => lambda { |row| row[:b] == "not something_else" }}}, [:a, :b, :c])
104
88
  new_row[0].should == "blah"
105
89
 
106
90
  #if true, #unless false
107
- new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], { :a=> { :type => :fixed, :string => "123", :if => lambda {|row| row[:a] == "blah"}, :unless => lambda {|row| row[:b] == "not something_else"} }}, [:a, :b, :c])
91
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => lambda { |row| row[:a] == "blah" }, :unless => lambda { |row| row[:b] == "not something_else" }}}, [:a, :b, :c])
108
92
  new_row[0].should == "123"
109
93
 
110
94
  #if false, #unless true
111
- new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], { :a=> { :type => :fixed, :string => "123", :if => lambda {|row| row[:a] == "not blah"}, :unless => lambda {|row| row[:b] == "something_else"} }}, [:a, :b, :c])
95
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a=> {:type => :fixed, :string => "123", :if => lambda { |row| row[:a] == "not blah" }, :unless => lambda { |row| row[:b] == "something_else" }}}, [:a, :b, :c])
112
96
  new_row[0].should == "blah"
113
97
  end
114
98
  end
115
99
 
116
100
  it "should be able to generate random integers in ranges" do
117
- new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], { :c => { :type => :integer, :between => 10..100 }}, [:a, :b, :c])
101
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:c => {:type => :integer, :between => 10..100}}, [:a, :b, :c])
118
102
  new_row.length.should == 3
119
103
  new_row[2].to_i.to_s.should == new_row[2] # It should be an integer.
120
104
  new_row[2].should_not == "5"
121
105
  end
122
106
 
123
107
  it "should be able to substitute fixed strings" do
124
- new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], { :b => { :type => :fixed, :string => "hello" }}, [:a, :b, :c])
108
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => {:type => :fixed, :string => "hello"}}, [:a, :b, :c])
125
109
  new_row.length.should == 3
126
110
  new_row[1].should == "hello"
127
111
  end
128
112
 
113
+ it "should be able to substitute a proc that returns a string" do
114
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => {:type => :fixed, :string => proc { "Hello World" }}}, [:a, :b, :c])
115
+ new_row.length.should == 3
116
+ new_row[1].should == "Hello World"
117
+ end
118
+
119
+ it "should provide the row to the proc" do
120
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => {:type => :fixed, :string => proc { |a| a[:b] }}}, [:a, :b, :c])
121
+ new_row.length.should == 3
122
+ new_row[1].should == "something_else"
123
+ end
124
+
129
125
  it "should be able to substitute fixed strings from a random set" do
130
126
  looking_for = ["hello", "world"]
131
127
  original_looking_for = looking_for.dup
132
128
  guard = 0
133
129
  while !looking_for.empty? && guard < 1000
134
- new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], { :a => { :type => :fixed, :one_of => ["hello", "world"] }}, [:a, :b, :c])
130
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => {:type => :fixed, :one_of => ["hello", "world"]}}, [:a, :b, :c])
135
131
  new_row.length.should == 3
136
132
  original_looking_for.should include(new_row[0])
137
133
  looking_for.delete new_row[0]
@@ -140,12 +136,135 @@ describe MyObfuscate do
140
136
  looking_for.should be_empty
141
137
  end
142
138
 
139
+ it "should treat a symbol in the column definition as an implicit { :type => symbol }" do
140
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => :null, :a => :keep}, [:a, :b, :c])
141
+ new_row.length.should == 3
142
+ new_row[0].should == "blah"
143
+ new_row[1].should == nil
144
+ end
145
+
143
146
  it "should be able to set things NULL" do
144
- new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], { :b => { :type => :null }}, [:a, :b, :c])
147
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => {:type => :null}}, [:a, :b, :c])
145
148
  new_row.length.should == 3
146
149
  new_row[1].should == nil
147
150
  end
148
151
 
152
+ it "should be able to :keep the value the same" do
153
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => {:type => :keep}}, [:a, :b, :c])
154
+ new_row.length.should == 3
155
+ new_row[1].should == "something_else"
156
+ end
157
+
158
+ it "should keep the value when given an unknown type, but should display a warning" do
159
+ $stderr = error_output = StringIO.new
160
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:b => {:type => :unknown_type}}, [:a, :b, :c])
161
+ $stderr = STDERR
162
+ new_row.length.should == 3
163
+ new_row[1].should == "something_else"
164
+ error_output.rewind
165
+ error_output.read.should =~ /Keeping a column value by.*?unknown_type/
166
+ end
167
+
168
+ it "should be able to substitute lorem ipsum text" do
169
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :lorem, :b => {:type => :lorem, :number => 2}}, [:a, :b, :c])
170
+ new_row.length.should == 3
171
+ new_row[0].should_not == "blah"
172
+ new_row[0].should_not =~ /\w\.(?!\Z)/
173
+ new_row[1].should_not == "something_else"
174
+ new_row[1].should =~ /\w\.(?!\Z)/
175
+ end
176
+
177
+ it "should be able to generate an :company" do
178
+ new_row = MyObfuscate.apply_table_config(["Smith and Sons", "something_else", "5"], {:a => :company}, [:a, :b, :c])
179
+ new_row.length.should == 3
180
+ new_row[0].should_not == "Smith and Sons"
181
+ new_row[0].should =~ /\w+/
182
+ end
183
+
184
+ it "should be able to generate an :url" do
185
+ new_row = MyObfuscate.apply_table_config(["http://mystuff.blogger.com", "something_else", "5"], {:a => :url}, [:a, :b, :c])
186
+ new_row.length.should == 3
187
+ new_row[0].should_not == "http://mystuff.blogger.com"
188
+ new_row[0].should =~ /http:\/\/\w+/
189
+ end
190
+
191
+ it "should be able to generate an :ipv4" do
192
+ new_row = MyObfuscate.apply_table_config(["1.2.3.4", "something_else", "5"], {:a => :ipv4}, [:a, :b, :c])
193
+ new_row.length.should == 3
194
+ new_row[0].should_not == "1.2.3.4"
195
+ new_row[0].should =~ /\d+\.\d+\.\d+\.\d+/
196
+ end
197
+
198
+ it "should be able to generate an :ipv6" do
199
+ new_row = MyObfuscate.apply_table_config(["fe80:0000:0000:0000:0202:b3ff:fe1e:8329", "something_else", "5"], {:a => :ipv6}, [:a, :b, :c])
200
+ new_row.length.should == 3
201
+ new_row[0].should_not == "fe80:0000:0000:0000:0202:b3ff:fe1e:8329"
202
+ new_row[0].should =~ /[0-9a-f:]+/
203
+ end
204
+
205
+ it "should be able to generate an :address" do
206
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :address}, [:a, :b, :c])
207
+ new_row.length.should == 3
208
+ new_row[0].should_not == "blah"
209
+ new_row[0].should =~ /\d+ \w/
210
+ end
211
+
212
+ it "should be able to generate a :name" do
213
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :name}, [:a, :b, :c])
214
+ new_row.length.should == 3
215
+ new_row[0].should_not == "blah"
216
+ new_row[0].should =~ / /
217
+ end
218
+
219
+ it "should be able to generate just a street address" do
220
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :street_address}, [:a, :b, :c])
221
+ new_row.length.should == 3
222
+ new_row[0].should_not == "blah"
223
+ new_row[0].should =~ /\d+ \w/
224
+ end
225
+
226
+ it "should be able to generate a city" do
227
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :city}, [:a, :b, :c])
228
+ new_row.length.should == 3
229
+ new_row[0].should_not == "blah"
230
+ end
231
+
232
+ it "should be able to generate a state" do
233
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :state}, [:a, :b, :c])
234
+ new_row.length.should == 3
235
+ new_row[0].should_not == "blah"
236
+ end
237
+
238
+ it "should be able to generate a zip code" do
239
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :zip_code}, [:a, :b, :c])
240
+ new_row.length.should == 3
241
+ new_row[0].should_not == "blah"
242
+ new_row[0].should =~ /\d+/
243
+ end
244
+
245
+ it "should be able to generate a phone number" do
246
+ new_row = MyObfuscate.apply_table_config(["blah", "something_else", "5"], {:a => :phone}, [:a, :b, :c])
247
+ new_row.length.should == 3
248
+ new_row[0].should_not == "blah"
249
+ new_row[0].should =~ /\d+/
250
+ end
251
+
252
+ describe "when faker generates values with quotes in them" do
253
+ before do
254
+ Faker::Address.stub(:city).and_return("O'ReillyTown")
255
+ Faker::Name.stub(:name).and_return("Foo O'Reilly")
256
+ Faker::Name.stub(:first_name).and_return("O'Foo")
257
+ Faker::Name.stub(:last_name).and_return("O'Reilly")
258
+ Faker::Lorem.stub(:sentences).with(any_args).and_return(["Foo bar O'Thingy"])
259
+ end
260
+
261
+ it "should remove single quotes from the value" do
262
+ new_row = MyObfuscate.apply_table_config(["address", "city", "first", "last", "fullname", "some text"],
263
+ {:a => :address, :b => :city, :c => :first_name, :d => :last_name, :e => :name, :f => :lorem},
264
+ [:a, :b, :c, :d, :e, :f])
265
+ new_row.each {|value| value.should_not include("'")}
266
+ end
267
+ end
149
268
  end
150
269
 
151
270
  describe "MyObfuscate.row_as_hash" do
@@ -155,91 +274,285 @@ describe MyObfuscate do
155
274
  end
156
275
 
157
276
  describe "#obfuscate" do
158
- context "when there is nothing to obfuscate" do
159
- it "should accept an IO object for input and output, and copy the input to the output" do
160
- ddo = MyObfuscate.new
161
- string = "hello, world\nsup?"
162
- input = StringIO.new(string)
163
- output = StringIO.new
164
- ddo.obfuscate(input, output)
165
- input.rewind
166
- output.rewind
167
- output.read.should == string
277
+ describe "when using MySQL" do
278
+ context "when there is nothing to obfuscate" do
279
+ it "should accept an IO object for input and output, and copy the input to the output" do
280
+ ddo = MyObfuscate.new
281
+ string = "hello, world\nsup?"
282
+ input = StringIO.new(string)
283
+ output = StringIO.new
284
+ ddo.obfuscate(input, output)
285
+ input.rewind
286
+ output.rewind
287
+ output.read.should == string
288
+ end
168
289
  end
169
- end
170
290
 
171
- context "when the dump to obfuscate is missing columns" do
172
- before do
173
- @database_dump = StringIO.new(<<-SQL)
291
+ context "when the dump to obfuscate is missing columns" do
292
+ before do
293
+ @database_dump = StringIO.new(<<-SQL)
174
294
  INSERT INTO `some_table` (`email`, `name`, `something`, `age`) VALUES ('bob@honk.com','bob', 'some\\'thin,ge())lse1', 25),('joe@joe.com','joe', 'somethingelse2', 54);
175
- SQL
176
- @ddo = MyObfuscate.new({
177
- :some_table => {
178
- :email => { :type => :email, :honk_email_skip => true },
179
- :name => { :type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS },
180
- :gender => { :type => :fixed, :string => "m" }
181
- }})
182
- @output = StringIO.new
183
- end
184
-
185
- it "should raise an error if a column name can't be found" do
186
- lambda {
187
- @ddo.obfuscate(@database_dump, @output)
188
- }.should raise_error
295
+ SQL
296
+ @ddo = MyObfuscate.new({
297
+ :some_table => {
298
+ :email => {:type => :email, :honk_email_skip => true},
299
+ :name => {:type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS},
300
+ :gender => {:type => :fixed, :string => "m"}
301
+ }})
302
+ @output = StringIO.new
303
+ end
304
+
305
+ it "should raise an error if a column name can't be found" do
306
+ lambda {
307
+ @ddo.obfuscate(@database_dump, @output)
308
+ }.should raise_error
309
+ end
189
310
  end
190
- end
191
311
 
192
- context "when there is something to obfuscate" do
193
- before do
194
- @database_dump = StringIO.new(<<-SQL)
312
+ context "when there is something to obfuscate" do
313
+ before do
314
+ @database_dump = StringIO.new(<<-SQL)
195
315
  INSERT INTO `some_table` (`email`, `name`, `something`, `age`) VALUES ('bob@honk.com','bob', 'some\\'thin,ge())lse1', 25),('joe@joe.com','joe', 'somethingelse2', 54),('dontmurderme@direwolf.com','direwolf', 'somethingelse3', 44);
196
316
  INSERT INTO `another_table` (`a`, `b`, `c`, `d`) VALUES (1,2,3,4), (5,6,7,8);
317
+ INSERT INTO `some_table_to_keep` (`a`, `b`, `c`, `d`) VALUES (1,2,3,4), (5,6,7,8);
197
318
  INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES ('hello','kjhjd^&dkjh', 'aawefjkafe'), ('hello1','kjhj!', 892938), ('hello2','moose!!', NULL);
198
319
  INSERT INTO `an_ignored_table` (`col`, `col2`) VALUES ('hello','kjhjd^&dkjh'), ('hello1','kjhj!'), ('hello2','moose!!');
199
- SQL
200
-
201
- @ddo = MyObfuscate.new({
202
- :some_table => {
203
- :email => { :type => :email, :skip_regexes => [/^[\w\.\_]+@honk\.com$/i, /^dontmurderme@direwolf.com$/] },
204
- :name => { :type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS },
205
- :age => { :type => :integer, :between => 10...80 }
206
- },
207
- :another_table => :truncate,
208
- :one_more_table => {
209
- # Note: fixed strings must be pre-SQL escaped!
210
- :password => { :type => :fixed, :string => "monkey" },
211
- :c => { :type => :null }
212
- }
213
- })
214
- @output = StringIO.new
215
- @ddo.obfuscate(@database_dump, @output)
216
- @output.rewind
217
- @output_string = @output.read
218
- end
219
-
220
- it "should be able to truncate tables" do
221
- @output_string.should_not include("INSERT INTO `another_table`")
222
- @output_string.should include("INSERT INTO `one_more_table`")
223
- end
224
-
225
- it "should ignore tables that it doesn't know about" do
226
- @output_string.should include("INSERT INTO `an_ignored_table` (`col`, `col2`) VALUES ('hello','kjhjd^&dkjh'), ('hello1','kjhj!'), ('hello2','moose!!');")
227
- end
228
-
229
- it "should obfuscate the tables" do
230
- @output_string.should include("INSERT INTO `some_table` (`email`, `name`, `something`, `age`) VALUES (")
231
- @output_string.should include("INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES (")
232
- @output_string.should include("'some\\'thin,ge())lse1'")
233
- @output_string.should include("INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES ('hello','monkey',NULL),('hello1','monkey',NULL),('hello2','monkey',NULL);")
234
- @output_string.should_not include("INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES ('hello','kjhjd^&dkjh', 'aawefjkafe'), ('hello1','kjhj!', 892938), ('hello2','moose!!', NULL);")
235
- @output_string.should_not include("INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES ('hello','kjhjd^&dkjh','aawefjkafe'),('hello1','kjhj!',892938),('hello2','moose!!',NULL);")
236
- @output_string.should_not include("INSERT INTO `some_table` (`email`, `name`, `something`, `age`) VALUES ('bob@honk.com','bob', 'some\\'thin,ge())lse1', 25),('joe@joe.com','joe', 'somethingelse2', 54);")
237
- end
238
-
239
- it "honors a special case: on the people table, rows with anything@honk.com in a slot marked with :honk_email_skip do not change this slot" do
240
- @output_string.should include("('bob@honk.com',")
241
- @output_string.should include("('dontmurderme@direwolf.com',")
242
- @output_string.should_not include("joe@joe.com")
320
+ SQL
321
+
322
+ @ddo = MyObfuscate.new({
323
+ :some_table => {
324
+ :email => {:type => :email, :skip_regexes => [/^[\w\.\_]+@honk\.com$/i, /^dontmurderme@direwolf.com$/]},
325
+ :name => {:type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS},
326
+ :age => {:type => :integer, :between => 10...80}
327
+ },
328
+ :another_table => :truncate,
329
+ :some_table_to_keep => :keep,
330
+ :one_more_table => {
331
+ # Note: fixed strings must be pre-SQL escaped!
332
+ :password => {:type => :fixed, :string => "monkey"},
333
+ :c => {:type => :null}
334
+ }
335
+ })
336
+ @output = StringIO.new
337
+ $stderr = @error_output = StringIO.new
338
+ @ddo.obfuscate(@database_dump, @output)
339
+ $stderr = STDERR
340
+ @output.rewind
341
+ @output_string = @output.read
342
+ end
343
+
344
+ it "should be able to truncate tables" do
345
+ @output_string.should_not include("INSERT INTO `another_table`")
346
+ @output_string.should include("INSERT INTO `one_more_table`")
347
+ end
348
+
349
+ it "should be able to declare tables to keep" do
350
+ @output_string.should include("INSERT INTO `some_table_to_keep` (`a`, `b`, `c`, `d`) VALUES (1,2,3,4), (5,6,7,8);")
351
+ end
352
+
353
+ it "should ignore tables that it doesn't know about, but should warn" do
354
+ @output_string.should include("INSERT INTO `an_ignored_table` (`col`, `col2`) VALUES ('hello','kjhjd^&dkjh'), ('hello1','kjhj!'), ('hello2','moose!!');")
355
+ @error_output.rewind
356
+ @error_output.read.should =~ /an_ignored_table was not specified in the config/
357
+ end
358
+
359
+ it "should obfuscate the tables" do
360
+ @output_string.should include("INSERT INTO `some_table` (`email`, `name`, `something`, `age`) VALUES (")
361
+ @output_string.should include("INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES (")
362
+ @output_string.should include("'some\\'thin,ge())lse1'")
363
+ @output_string.should include("INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES ('hello','monkey',NULL),('hello1','monkey',NULL),('hello2','monkey',NULL);")
364
+ @output_string.should_not include("INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES ('hello','kjhjd^&dkjh', 'aawefjkafe'), ('hello1','kjhj!', 892938), ('hello2','moose!!', NULL);")
365
+ @output_string.should_not include("INSERT INTO `one_more_table` (`a`, `password`, `c`, `d,d`) VALUES ('hello','kjhjd^&dkjh','aawefjkafe'),('hello1','kjhj!',892938),('hello2','moose!!',NULL);")
366
+ @output_string.should_not include("INSERT INTO `some_table` (`email`, `name`, `something`, `age`) VALUES ('bob@honk.com','bob', 'some\\'thin,ge())lse1', 25),('joe@joe.com','joe', 'somethingelse2', 54);")
367
+ end
368
+
369
+ it "honors a special case: on the people table, rows with anything@honk.com in a slot marked with :honk_email_skip do not change this slot" do
370
+ @output_string.should include("('bob@honk.com',")
371
+ @output_string.should include("('dontmurderme@direwolf.com',")
372
+ @output_string.should_not include("joe@joe.com")
373
+ end
374
+ end
375
+
376
+ context "when fail_on_unspecified_columns is set to true" do
377
+ before do
378
+ @database_dump = StringIO.new(<<-SQL)
379
+ INSERT INTO `some_table` (`email`, `name`, `something`, `age`) VALUES ('bob@honk.com','bob', 'some\\'thin,ge())lse1', 25),('joe@joe.com','joe', 'somethingelse2', 54),('dontmurderme@direwolf.com','direwolf', 'somethingelse3', 44);
380
+ SQL
381
+
382
+ @ddo = MyObfuscate.new({
383
+ :some_table => {
384
+ :email => {:type => :email, :skip_regexes => [/^[\w\.\_]+@honk\.com$/i, /^dontmurderme@direwolf.com$/]},
385
+ :name => {:type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS},
386
+ :age => {:type => :integer, :between => 10...80}
387
+ }
388
+ })
389
+ @ddo.fail_on_unspecified_columns = true
390
+ end
391
+
392
+ it "should raise an exception when an unspecified column is found" do
393
+ lambda {
394
+ @ddo.obfuscate(@database_dump, StringIO.new)
395
+ }.should raise_error(/column 'something' defined/i)
396
+ end
397
+
398
+ it "should accept columns defined in globally_kept_columns" do
399
+ @ddo.globally_kept_columns = %w[something]
400
+ lambda {
401
+ @ddo.obfuscate(@database_dump, StringIO.new)
402
+ }.should_not raise_error
403
+ end
404
+ end
405
+ end
406
+
407
+ describe "when using MS SQL Server" do
408
+ context "when there is nothing to obfuscate" do
409
+ it "should accept an IO object for input and output, and copy the input to the output" do
410
+ ddo = MyObfuscate.new
411
+ ddo.database_type = :sql_server
412
+ string = "hello, world\nsup?"
413
+ input = StringIO.new(string)
414
+ output = StringIO.new
415
+ ddo.obfuscate(input, output)
416
+ input.rewind
417
+ output.rewind
418
+ output.read.should == string
419
+ end
420
+ end
421
+
422
+ context "when the dump to obfuscate is missing columns" do
423
+ before do
424
+ @database_dump = StringIO.new(<<-SQL)
425
+ INSERT [dbo].[some_table] ([email], [name], [something], [age]) VALUES ('bob@honk.com','bob', 'some''thin,ge())lse1', 25);
426
+ SQL
427
+ @ddo = MyObfuscate.new({
428
+ :some_table => {
429
+ :email => {:type => :email, :honk_email_skip => true},
430
+ :name => {:type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS},
431
+ :gender => {:type => :fixed, :string => "m"}
432
+ }})
433
+ @ddo.database_type = :sql_server
434
+ @output = StringIO.new
435
+ end
436
+
437
+ it "should raise an error if a column name can't be found" do
438
+ lambda {
439
+ @ddo.obfuscate(@database_dump, @output)
440
+ }.should raise_error
441
+ end
442
+ end
443
+
444
+ context "when there is something to obfuscate" do
445
+ before do
446
+ @database_dump = StringIO.new(<<-SQL)
447
+ INSERT [dbo].[some_table] ([email], [name], [something], [age], [bday]) VALUES (N'bob@honk.com',N'bob', N'some''thin,ge())lse1', 25, CAST(0x00009E1A00000000 AS DATETIME));
448
+ INSERT [dbo].[some_table] ([email], [name], [something], [age], [bday]) VALUES (N'joe@joe.com',N'joe', N'somethingelse2', 54, CAST(0x00009E1A00000000 AS DATETIME));
449
+ INSERT [dbo].[some_table] ([email], [name], [something], [age], [bday]) VALUES (N'dontmurderme@direwolf.com',N'direwolf', N'somethingelse3', 44, CAST(0x00009E1A00000000 AS DATETIME));
450
+ INSERT [dbo].[another_table] ([a], [b], [c], [d]) VALUES (1,2,3,4);
451
+ INSERT [dbo].[another_table] ([a], [b], [c], [d]) VALUES (5,6,7,8);
452
+ INSERT [dbo].[some_table_to_keep] ([a], [b], [c], [d]) VALUES (1,2,3,4);
453
+ INSERT [dbo].[some_table_to_keep] ([a], [b], [c], [d]) VALUES (5,6,7,8);
454
+ INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello',N'kjhjd^&dkjh', N'aawefjkafe');
455
+ INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello1',N'kjhj!', 892938);
456
+ INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello2',N'moose!!', NULL);
457
+ INSERT [dbo].[an_ignored_table] ([col], [col2]) VALUES (N'hello',N'kjhjd^&dkjh');
458
+ INSERT [dbo].[an_ignored_table] ([col], [col2]) VALUES (N'hello1',N'kjhj!');
459
+ INSERT [dbo].[an_ignored_table] ([col], [col2]) VALUES (N'hello2',N'moose!!');
460
+ SQL
461
+
462
+ @ddo = MyObfuscate.new({
463
+ :some_table => {
464
+ :email => {:type => :email, :skip_regexes => [/^[\w\.\_]+@honk\.com$/i, /^dontmurderme@direwolf.com$/]},
465
+ :name => {:type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS},
466
+ :age => {:type => :integer, :between => 10...80},
467
+ :bday => :keep
468
+ },
469
+ :another_table => :truncate,
470
+ :some_table_to_keep => :keep,
471
+ :one_more_table => {
472
+ # Note: fixed strings must be pre-SQL escaped!
473
+ :password => {:type => :fixed, :string => "monkey"},
474
+ :c => {:type => :null}
475
+ }
476
+ })
477
+ @ddo.database_type = :sql_server
478
+
479
+ @output = StringIO.new
480
+ $stderr = @error_output = StringIO.new
481
+ @ddo.obfuscate(@database_dump, @output)
482
+ $stderr = STDERR
483
+ @output.rewind
484
+ @output_string = @output.read
485
+ end
486
+
487
+ it "should be able to truncate tables" do
488
+ @output_string.should_not include("INSERT [dbo].[another_table]")
489
+ @output_string.should include("INSERT [dbo].[one_more_table]")
490
+ end
491
+
492
+ it "should be able to declare tables to keep" do
493
+ @output_string.should include("INSERT [dbo].[some_table_to_keep] ([a], [b], [c], [d]) VALUES (1,2,3,4);")
494
+ @output_string.should include("INSERT [dbo].[some_table_to_keep] ([a], [b], [c], [d]) VALUES (5,6,7,8);")
495
+ end
496
+
497
+ it "should ignore tables that it doesn't know about, but should warn" do
498
+ @output_string.should include("INSERT [dbo].[an_ignored_table] ([col], [col2]) VALUES (N'hello',N'kjhjd^&dkjh');")
499
+ @output_string.should include("INSERT [dbo].[an_ignored_table] ([col], [col2]) VALUES (N'hello1',N'kjhj!');")
500
+ @output_string.should include("INSERT [dbo].[an_ignored_table] ([col], [col2]) VALUES (N'hello2',N'moose!!');")
501
+ @error_output.rewind
502
+ @error_output.read.should =~ /an_ignored_table was not specified in the config/
503
+ end
504
+
505
+ it "should obfuscate the tables" do
506
+ @output_string.should include("INSERT [dbo].[some_table] ([email], [name], [something], [age], [bday]) VALUES (")
507
+ @output_string.should include("CAST(0x00009E1A00000000 AS DATETIME)")
508
+ @output_string.should include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (")
509
+ @output_string.should include("'some''thin,ge())lse1'")
510
+ @output_string.should include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello',N'monkey',NULL);")
511
+ @output_string.should include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello1',N'monkey',NULL);")
512
+ @output_string.should include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello2',N'monkey',NULL);")
513
+ @output_string.should_not include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello',N'kjhjd^&dkjh', N'aawefjkafe');")
514
+ @output_string.should_not include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello1',N'kjhj!', 892938);")
515
+ @output_string.should_not include("INSERT [dbo].[one_more_table] ([a], [password], [c], [d,d]) VALUES (N'hello2',N'moose!!', NULL);")
516
+ @output_string.should_not include("INSERT [dbo].[some_table] ([email], [name], [something], [age]) VALUES (N'bob@honk.com',N'bob', N'some''thin,ge())lse1', 25, CAST(0x00009E1A00000000 AS DATETIME));")
517
+ @output_string.should_not include("INSERT [dbo].[some_table] ([email], [name], [something], [age]) VALUES (N'joe@joe.com',N'joe', N'somethingelse2', 54, CAST(0x00009E1A00000000 AS DATETIME));")
518
+ end
519
+
520
+ it "honors a special case: on the people table, rows with anything@honk.com in a slot marked with :honk_email_skip do not change this slot" do
521
+ @output_string.should include("(N'bob@honk.com',")
522
+ @output_string.should include("(N'dontmurderme@direwolf.com',")
523
+ @output_string.should_not include("joe@joe.com")
524
+ end
525
+ end
526
+
527
+ context "when fail_on_unspecified_columns is set to true" do
528
+ before do
529
+ @database_dump = StringIO.new(<<-SQL)
530
+ INSERT INTO [dbo].[some_table] ([email], [name], [something], [age]) VALUES ('bob@honk.com','bob', 'some''thin,ge())lse1', 25);
531
+ SQL
532
+
533
+ @ddo = MyObfuscate.new({
534
+ :some_table => {
535
+ :email => {:type => :email, :skip_regexes => [/^[\w\.\_]+@honk\.com$/i, /^dontmurderme@direwolf.com$/]},
536
+ :name => {:type => :string, :length => 8, :chars => MyObfuscate::USERNAME_CHARS},
537
+ :age => {:type => :integer, :between => 10...80}
538
+ }
539
+ })
540
+ @ddo.database_type = :sql_server
541
+ @ddo.fail_on_unspecified_columns = true
542
+ end
543
+
544
+ it "should raise an exception when an unspecified column is found" do
545
+ lambda {
546
+ @ddo.obfuscate(@database_dump, StringIO.new)
547
+ }.should raise_error(/column 'something' defined/i)
548
+ end
549
+
550
+ it "should accept columns defined in globally_kept_columns" do
551
+ @ddo.globally_kept_columns = %w[something]
552
+ lambda {
553
+ @ddo.obfuscate(@database_dump, StringIO.new)
554
+ }.should_not raise_error
555
+ end
243
556
  end
244
557
  end
245
558
  end